#include "config.h"
#endif
+#include <helper/log.h>
+#include <helper/time_support.h>
#include "target/target.h"
#include "target/algorithm.h"
#include "target/target_type.h"
-#include "log.h"
+#include <target/smp.h>
#include "jtag/jtag.h"
#include "target/register.h"
#include "target/breakpoints.h"
-#include "helper/time_support.h"
#include "riscv.h"
#include "gdb_regs.h"
#include "rtos/rtos.h"
+#include "debug_defines.h"
+#include <helper/bits.h>
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
-#define DIM(x) (sizeof(x)/sizeof(*x))
-
/* Constants for legacy SiFive hardware breakpoints. */
#define CSR_BPCONTROL_X (1<<0)
#define CSR_BPCONTROL_W (1<<1)
bscan_tunnel_type_t bscan_tunnel_type;
int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */
-static uint8_t bscan_zero[4] = {0};
-static uint8_t bscan_one[4] = {1};
+static const uint8_t bscan_zero[4] = {0};
+static const uint8_t bscan_one[4] = {1};
-uint8_t ir_user4[4] = {0x23};
+uint8_t ir_user4[4];
struct scan_field select_user4 = {
.in_value = NULL,
.out_value = ir_user4
}
};
struct scan_field *bscan_tunnel_nested_tap_select_dmi = _bscan_tunnel_nested_tap_select_dmi;
-uint32_t bscan_tunnel_nested_tap_select_dmi_num_fields = DIM(_bscan_tunnel_nested_tap_select_dmi);
+uint32_t bscan_tunnel_nested_tap_select_dmi_num_fields = ARRAY_SIZE(_bscan_tunnel_nested_tap_select_dmi);
struct scan_field *bscan_tunnel_data_register_select_dmi = _bscan_tunnel_data_register_select_dmi;
-uint32_t bscan_tunnel_data_register_select_dmi_num_fields = DIM(_bscan_tunnel_data_register_select_dmi);
+uint32_t bscan_tunnel_data_register_select_dmi_num_fields = ARRAY_SIZE(_bscan_tunnel_data_register_select_dmi);
struct trigger {
uint64_t address;
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
-bool riscv_prefer_sba;
bool riscv_enable_virt2phys = true;
bool riscv_ebreakm = true;
bool riscv_ebreaks = true;
bool riscv_enable_virtual;
-typedef struct {
- uint16_t low, high;
-} range_t;
-
-/* In addition to the ones in the standard spec, we'll also expose additional
- * CSRs in this list.
- * The list is either NULL, or a series of ranges (inclusive), terminated with
- * 1,0. */
-range_t *expose_csr;
-/* Same, but for custom registers. */
-range_t *expose_custom;
-
static enum {
RO_NORMAL,
RO_REVERSED
} resume_order;
-virt2phys_info_t sv32 = {
+const virt2phys_info_t sv32 = {
.name = "Sv32",
.va_bits = 32,
.level = 2,
.pa_ppn_mask = {0x3ff, 0xfff},
};
-virt2phys_info_t sv39 = {
+const virt2phys_info_t sv39 = {
.name = "Sv39",
.va_bits = 39,
.level = 3,
.pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
};
-virt2phys_info_t sv48 = {
+const virt2phys_info_t sv48 = {
.name = "Sv48",
.va_bits = 48,
.level = 4,
.pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
};
+void riscv_sample_buf_maybe_add_timestamp(struct target *target, bool before)
+{
+ RISCV_INFO(r);
+ uint32_t now = timeval_ms() & 0xffffffff;
+ if (r->sample_buf.used + 5 < r->sample_buf.size) {
+ if (before)
+ r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE;
+ else
+ r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_AFTER;
+ r->sample_buf.buf[r->sample_buf.used++] = now & 0xff;
+ r->sample_buf.buf[r->sample_buf.used++] = (now >> 8) & 0xff;
+ r->sample_buf.buf[r->sample_buf.used++] = (now >> 16) & 0xff;
+ r->sample_buf.buf[r->sample_buf.used++] = (now >> 24) & 0xff;
+ }
+}
+
static int riscv_resume_go_all_harts(struct target *target);
void select_dmi_via_bscan(struct target *target)
tunneled_dr[0].in_value = NULL;
}
jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE);
- jtag_add_dr_scan(target->tap, DIM(tunneled_ir), tunneled_ir, TAP_IDLE);
- jtag_add_dr_scan(target->tap, DIM(tunneled_dr), tunneled_dr, TAP_IDLE);
+ jtag_add_dr_scan(target->tap, ARRAY_SIZE(tunneled_ir), tunneled_ir, TAP_IDLE);
+ jtag_add_dr_scan(target->tap, ARRAY_SIZE(tunneled_dr), tunneled_dr, TAP_IDLE);
select_dmi_via_bscan(target);
int retval = jtag_execute_queue();
return in;
}
-
-
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
{
struct scan_field field;
static struct target_type *get_target_type(struct target *target)
{
- riscv_info_t *info = (riscv_info_t *) target->arch_info;
-
- if (!info) {
+ if (!target->arch_info) {
LOG_ERROR("Target has not been initialized");
return NULL;
}
+ RISCV_INFO(info);
switch (info->dtm_version) {
case 0:
return &riscv011_target;
}
}
+static int riscv_create_target(struct target *target, Jim_Interp *interp)
+{
+ LOG_DEBUG("riscv_create_target()");
+ target->arch_info = calloc(1, sizeof(struct riscv_info));
+ if (!target->arch_info) {
+ LOG_ERROR("Failed to allocate RISC-V target structure.");
+ return ERROR_FAIL;
+ }
+ riscv_info_init(target, target->arch_info);
+ return ERROR_OK;
+}
+
static int riscv_init_target(struct command_context *cmd_ctx,
struct target *target)
{
LOG_DEBUG("riscv_init_target()");
- target->arch_info = calloc(1, sizeof(riscv_info_t));
- if (!target->arch_info)
- return ERROR_FAIL;
- riscv_info_t *info = (riscv_info_t *) target->arch_info;
- riscv_info_init(target, info);
+ RISCV_INFO(info);
info->cmd_ctx = cmd_ctx;
select_dtmcontrol.num_bits = target->tap->ir_length;
select_idcode.num_bits = target->tap->ir_length;
if (bscan_tunnel_ir_width != 0) {
+ assert(target->tap->ir_length >= 6);
+ uint32_t ir_user4_raw = 0x23 << (target->tap->ir_length - 6);
+ ir_user4[0] = (uint8_t)ir_user4_raw;
+ ir_user4[1] = (uint8_t)(ir_user4_raw >>= 8);
+ ir_user4[2] = (uint8_t)(ir_user4_raw >>= 8);
+ ir_user4[3] = (uint8_t)(ir_user4_raw >>= 8);
select_user4.num_bits = target->tap->ir_length;
bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width;
if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER)
/* Free the ones we allocated separately. */
for (unsigned i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
free(target->reg_cache->reg_list[i].arch_info);
+ for (unsigned int i = 0; i < target->reg_cache->num_regs; i++)
+ free(target->reg_cache->reg_list[i].value);
free(target->reg_cache->reg_list);
}
free(target->reg_cache);
static void riscv_deinit_target(struct target *target)
{
LOG_DEBUG("riscv_deinit_target()");
+
+ struct riscv_info *info = target->arch_info;
struct target_type *tt = get_target_type(target);
- if (tt) {
+
+ if (tt && info && info->version_specific)
tt->deinit_target(target);
- riscv_info_t *info = (riscv_info_t *) target->arch_info;
- free(info->reg_names);
- free(info);
- }
riscv_free_registers(target);
+ if (!info)
+ return;
+
+ range_list_t *entry, *tmp;
+ list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) {
+ free(entry->name);
+ free(entry);
+ }
+
+ list_for_each_entry_safe(entry, tmp, &info->expose_custom, list) {
+ free(entry->name);
+ free(entry);
+ }
+
+ free(info->reg_names);
+ free(target->arch_info);
+
target->arch_info = NULL;
}
trigger->unique_id = breakpoint->unique_id;
}
-static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
+static int maybe_add_trigger_t1(struct target *target,
struct trigger *trigger, uint64_t tdata1)
{
RISCV_INFO(r);
tdata1 = set_field(tdata1, bpcontrol_w, trigger->write);
tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute);
tdata1 = set_field(tdata1, bpcontrol_u,
- !!(r->misa[hartid] & (1 << ('U' - 'A'))));
+ !!(r->misa & BIT('U' - 'A')));
tdata1 = set_field(tdata1, bpcontrol_s,
- !!(r->misa[hartid] & (1 << ('S' - 'A'))));
+ !!(r->misa & BIT('S' - 'A')));
tdata1 = set_field(tdata1, bpcontrol_h,
- !!(r->misa[hartid] & (1 << ('H' - 'A'))));
+ !!(r->misa & BIT('H' - 'A')));
tdata1 |= bpcontrol_m;
tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+ riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
riscv_reg_t tdata1_rb;
- if (riscv_get_register_on_hart(target, &tdata1_rb, hartid,
- GDB_REGNO_TDATA1) != ERROR_OK)
+ if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
return ERROR_FAIL;
LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
PRIx64 " to tdata1 it contains 0x%" PRIx64,
tdata1, tdata1_rb);
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
+ riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
return ERROR_OK;
}
-static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
+static int maybe_add_trigger_t2(struct target *target,
struct trigger *trigger, uint64_t tdata1)
{
RISCV_INFO(r);
MCONTROL_ACTION_DEBUG_MODE);
tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
tdata1 |= MCONTROL_M;
- if (r->misa[hartid] & (1 << ('H' - 'A')))
- tdata1 |= MCONTROL_H;
- if (r->misa[hartid] & (1 << ('S' - 'A')))
+ if (r->misa & (1 << ('S' - 'A')))
tdata1 |= MCONTROL_S;
- if (r->misa[hartid] & (1 << ('U' - 'A')))
+ if (r->misa & (1 << ('U' - 'A')))
tdata1 |= MCONTROL_U;
if (trigger->execute)
if (trigger->write)
tdata1 |= MCONTROL_STORE;
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+ riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
uint64_t tdata1_rb;
- int result = riscv_get_register_on_hart(target, &tdata1_rb, hartid, GDB_REGNO_TDATA1);
+ int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
if (result != ERROR_OK)
return result;
LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
PRIx64 " to tdata1 it contains 0x%" PRIx64,
tdata1, tdata1_rb);
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
+ riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
return ERROR_OK;
}
-static int add_trigger(struct target *target, struct trigger *trigger)
+static int maybe_add_trigger_t6(struct target *target,
+ struct trigger *trigger, uint64_t tdata1)
{
RISCV_INFO(r);
- if (riscv_enumerate_triggers(target) != ERROR_OK)
- return ERROR_FAIL;
+ /* tselect is already set */
+ if (tdata1 & (CSR_MCONTROL6_EXECUTE | CSR_MCONTROL6_STORE | CSR_MCONTROL6_LOAD)) {
+ /* Trigger is already in use, presumably by user code. */
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
- /* In RTOS mode, we need to set the same trigger in the same slot on every
- * hart, to keep up the illusion that each hart is a thread running on the
- * same core. */
+ /* address/data match trigger */
+ tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
+ tdata1 = set_field(tdata1, CSR_MCONTROL6_ACTION,
+ MCONTROL_ACTION_DEBUG_MODE);
+ tdata1 = set_field(tdata1, CSR_MCONTROL6_MATCH, MCONTROL_MATCH_EQUAL);
+ tdata1 |= CSR_MCONTROL6_M;
+ if (r->misa & (1 << ('H' - 'A')))
+ tdata1 |= CSR_MCONTROL6_VS | CSR_MCONTROL6_VU;
+ if (r->misa & (1 << ('S' - 'A')))
+ tdata1 |= CSR_MCONTROL6_S;
+ if (r->misa & (1 << ('U' - 'A')))
+ tdata1 |= CSR_MCONTROL6_U;
- /* Otherwise, we just set the trigger on the one hart this target deals
- * with. */
+ if (trigger->execute)
+ tdata1 |= CSR_MCONTROL6_EXECUTE;
+ if (trigger->read)
+ tdata1 |= CSR_MCONTROL6_LOAD;
+ if (trigger->write)
+ tdata1 |= CSR_MCONTROL6_STORE;
- riscv_reg_t tselect[RISCV_MAX_HARTS];
+ riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
- int first_hart = -1;
- for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
- if (!riscv_hart_enabled(target, hartid))
- continue;
- if (first_hart < 0)
- first_hart = hartid;
- int result = riscv_get_register_on_hart(target, &tselect[hartid],
- hartid, GDB_REGNO_TSELECT);
- if (result != ERROR_OK)
- return result;
+ uint64_t tdata1_rb;
+ int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
+ if (result != ERROR_OK)
+ return result;
+ LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+
+ if (tdata1 != tdata1_rb) {
+ LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
+ PRIx64 " to tdata1 it contains 0x%" PRIx64,
+ tdata1, tdata1_rb);
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
- assert(first_hart >= 0);
+
+ riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
+
+ return ERROR_OK;
+}
+
+static int add_trigger(struct target *target, struct trigger *trigger)
+{
+ RISCV_INFO(r);
+
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ riscv_reg_t tselect;
+ if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+ return ERROR_FAIL;
unsigned int i;
- for (i = 0; i < r->trigger_count[first_hart]; i++) {
+ for (i = 0; i < r->trigger_count; i++) {
if (r->trigger_unique_id[i] != -1)
continue;
- riscv_set_register_on_hart(target, first_hart, GDB_REGNO_TSELECT, i);
+ riscv_set_register(target, GDB_REGNO_TSELECT, i);
uint64_t tdata1;
- int result = riscv_get_register_on_hart(target, &tdata1, first_hart,
- GDB_REGNO_TDATA1);
+ int result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
if (result != ERROR_OK)
return result;
int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
result = ERROR_OK;
- for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
- if (!riscv_hart_enabled(target, hartid))
- continue;
- if (hartid > first_hart)
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
- switch (type) {
- case 1:
- result = maybe_add_trigger_t1(target, hartid, trigger, tdata1);
- break;
- case 2:
- result = maybe_add_trigger_t2(target, hartid, trigger, tdata1);
- break;
- default:
- LOG_DEBUG("trigger %d has unknown type %d", i, type);
- continue;
- }
-
- if (result != ERROR_OK)
+ switch (type) {
+ case 1:
+ result = maybe_add_trigger_t1(target, trigger, tdata1);
+ break;
+ case 2:
+ result = maybe_add_trigger_t2(target, trigger, tdata1);
+ break;
+ case 6:
+ result = maybe_add_trigger_t6(target, trigger, tdata1);
+ break;
+ default:
+ LOG_DEBUG("trigger %d has unknown type %d", i, type);
continue;
}
break;
}
- for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
- if (!riscv_hart_enabled(target, hartid))
- continue;
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT,
- tselect[hartid]);
- }
+ riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
- if (i >= r->trigger_count[first_hart]) {
+ if (i >= r->trigger_count) {
LOG_ERROR("Couldn't find an available hardware trigger.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
return ERROR_OK;
}
+/**
+ * Write one memory item of given "size". Use memory access of given "access_size".
+ * Utilize read-modify-write, if needed.
+ * */
+static int write_by_given_size(struct target *target, target_addr_t address,
+ uint32_t size, uint8_t *buffer, uint32_t access_size)
+{
+ assert(size == 1 || size == 2 || size == 4 || size == 8);
+ assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8);
+
+ if (access_size <= size && address % access_size == 0)
+ /* Can do the memory access directly without a helper buffer. */
+ return target_write_memory(target, address, access_size, size / access_size, buffer);
+
+ unsigned int offset_head = address % access_size;
+ unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2;
+ uint8_t helper_buf[n_blocks * access_size];
+
+ /* Read from memory */
+ if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Modify and write back */
+ memcpy(helper_buf + offset_head, buffer, size);
+ return target_write_memory(target, address - offset_head, access_size, n_blocks, helper_buf);
+}
+
+/**
+ * Read one memory item of given "size". Use memory access of given "access_size".
+ * Read larger section of memory and pick out the required portion, if needed.
+ * */
+static int read_by_given_size(struct target *target, target_addr_t address,
+ uint32_t size, uint8_t *buffer, uint32_t access_size)
+{
+ assert(size == 1 || size == 2 || size == 4 || size == 8);
+ assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8);
+
+ if (access_size <= size && address % access_size == 0)
+ /* Can do the memory access directly without a helper buffer. */
+ return target_read_memory(target, address, access_size, size / access_size, buffer);
+
+ unsigned int offset_head = address % access_size;
+ unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2;
+ uint8_t helper_buf[n_blocks * access_size];
+
+ /* Read from memory */
+ if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Pick the requested portion from the buffer */
+ memcpy(buffer, helper_buf + offset_head, size);
+ return ERROR_OK;
+}
+
+/**
+ * Write one memory item using any memory access size that will work.
+ * Utilize read-modify-write, if needed.
+ * */
+int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
+{
+ assert(size == 1 || size == 2 || size == 4 || size == 8);
+
+ /* Find access size that correspond to data size and the alignment. */
+ unsigned int preferred_size = size;
+ while (address % preferred_size != 0)
+ preferred_size /= 2;
+
+ /* First try the preferred (most natural) access size. */
+ if (write_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK)
+ return ERROR_OK;
+
+ /* On failure, try other access sizes.
+ Minimize the number of accesses by trying first the largest size. */
+ for (unsigned int access_size = 8; access_size > 0; access_size /= 2) {
+ if (access_size == preferred_size)
+ /* Already tried this size. */
+ continue;
+
+ if (write_by_given_size(target, address, size, buffer, access_size) == ERROR_OK)
+ return ERROR_OK;
+ }
+
+ /* No access attempt succeeded. */
+ return ERROR_FAIL;
+}
+
+/**
+ * Read one memory item using any memory access size that will work.
+ * Read larger section of memory and pick out the required portion, if needed.
+ * */
+int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
+{
+ assert(size == 1 || size == 2 || size == 4 || size == 8);
+
+ /* Find access size that correspond to data size and the alignment. */
+ unsigned int preferred_size = size;
+ while (address % preferred_size != 0)
+ preferred_size /= 2;
+
+ /* First try the preferred (most natural) access size. */
+ if (read_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK)
+ return ERROR_OK;
+
+ /* On failure, try other access sizes.
+ Minimize the number of accesses by trying first the largest size. */
+ for (unsigned int access_size = 8; access_size > 0; access_size /= 2) {
+ if (access_size == preferred_size)
+ /* Already tried this size. */
+ continue;
+
+ if (read_by_given_size(target, address, size, buffer, access_size) == ERROR_OK)
+ return ERROR_OK;
+ }
+
+ /* No access attempt succeeded. */
+ return ERROR_FAIL;
+}
+
int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
{
LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address);
return ERROR_FAIL;
}
- if (target_read_memory(target, breakpoint->address, 2, breakpoint->length / 2,
- breakpoint->orig_instr) != ERROR_OK) {
+ /* Read the original instruction. */
+ if (riscv_read_by_any_size(
+ target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) {
LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR,
breakpoint->address);
return ERROR_FAIL;
uint8_t buff[4] = { 0 };
buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c());
- int const retval = target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2, buff);
-
- if (retval != ERROR_OK) {
+ /* Write the ebreak instruction. */
+ if (riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) {
LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%"
TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
return ERROR_FAIL;
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
- breakpoint->set = true;
+ breakpoint->is_set = true;
return ERROR_OK;
}
if (riscv_enumerate_triggers(target) != ERROR_OK)
return ERROR_FAIL;
- int first_hart = -1;
- for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
- if (!riscv_hart_enabled(target, hartid))
- continue;
- if (first_hart < 0) {
- first_hart = hartid;
- break;
- }
- }
- assert(first_hart >= 0);
-
unsigned int i;
- for (i = 0; i < r->trigger_count[first_hart]; i++) {
+ for (i = 0; i < r->trigger_count; i++) {
if (r->trigger_unique_id[i] == trigger->unique_id)
break;
}
- if (i >= r->trigger_count[first_hart]) {
+ if (i >= r->trigger_count) {
LOG_ERROR("Couldn't find the hardware resources used by hardware "
"trigger.");
return ERROR_FAIL;
}
LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i,
trigger->unique_id);
- for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
- if (!riscv_hart_enabled(target, hartid))
- continue;
- riscv_reg_t tselect;
- int result = riscv_get_register_on_hart(target, &tselect, hartid, GDB_REGNO_TSELECT);
- if (result != ERROR_OK)
- return result;
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
- }
+
+ riscv_reg_t tselect;
+ int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+ if (result != ERROR_OK)
+ return result;
+ riscv_set_register(target, GDB_REGNO_TSELECT, i);
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
r->trigger_unique_id[i] = -1;
return ERROR_OK;
struct breakpoint *breakpoint)
{
if (breakpoint->type == BKPT_SOFT) {
- if (target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2,
- breakpoint->orig_instr) != ERROR_OK) {
+ /* Write the original instruction. */
+ if (riscv_write_by_any_size(
+ target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) {
LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at "
"0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
return ERROR_FAIL;
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
- breakpoint->set = false;
+ breakpoint->is_set = false;
return ERROR_OK;
}
int result = add_trigger(target, &trigger);
if (result != ERROR_OK)
return result;
- watchpoint->set = true;
+ watchpoint->is_set = true;
return ERROR_OK;
}
int result = remove_trigger(target, &trigger);
if (result != ERROR_OK)
return result;
- watchpoint->set = false;
+ watchpoint->is_set = false;
return ERROR_OK;
}
{
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
{
RISCV_INFO(r);
LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
- if (r->is_halted == NULL)
+ if (!r->is_halted)
return oldriscv_step(target, current, address, handle_breakpoints);
else
return riscv_openocd_step(target, current, address, handle_breakpoints);
/* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
- riscv_info_t *info = (riscv_info_t *) target->arch_info;
+ RISCV_INFO(info);
uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION);
LOG_DEBUG(" version=0x%x", info->dtm_version);
struct target_type *tt = get_target_type(target);
- if (tt == NULL)
+ if (!tt)
return ERROR_FAIL;
int result = tt->init_target(info->cmd_ctx, target);
static int old_or_new_riscv_poll(struct target *target)
{
RISCV_INFO(r);
- if (r->is_halted == NULL)
+ if (!r->is_halted)
return oldriscv_poll(target);
else
return riscv_openocd_poll(target);
}
+int riscv_select_current_hart(struct target *target)
+{
+ return riscv_set_current_hartid(target, target->coreid);
+}
+
int halt_prep(struct target *target)
{
RISCV_INFO(r);
- for (int i = 0; i < riscv_count_harts(target); ++i) {
- if (!riscv_hart_enabled(target, i))
- continue;
- LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
- target->debug_reason);
- if (riscv_set_current_hartid(target, i) != ERROR_OK)
+ LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
+ target->debug_reason);
+ if (riscv_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_is_halted(target)) {
+ LOG_DEBUG("[%s] Hart is already halted (reason=%d).",
+ target_name(target), target->debug_reason);
+ } else {
+ if (r->halt_prep(target) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_is_halted(target)) {
- LOG_DEBUG("Hart %d is already halted (reason=%d).", i,
- target->debug_reason);
- } else {
- if (r->halt_prep(target) != ERROR_OK)
- return ERROR_FAIL;
- r->prepped = true;
- }
+ r->prepped = true;
}
+
return ERROR_OK;
}
int riscv_halt_go_all_harts(struct target *target)
{
RISCV_INFO(r);
- for (int i = 0; i < riscv_count_harts(target); ++i) {
- if (!riscv_hart_enabled(target, i))
- continue;
- if (riscv_set_current_hartid(target, i) != ERROR_OK)
+ if (riscv_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_is_halted(target)) {
+ LOG_DEBUG("[%s] Hart is already halted.", target_name(target));
+ } else {
+ if (r->halt_go(target) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_is_halted(target)) {
- LOG_DEBUG("Hart %d is already halted.", i);
- } else {
- if (r->halt_go(target) != ERROR_OK)
- return ERROR_FAIL;
- }
}
riscv_invalidate_register_cache(target);
int halt_go(struct target *target)
{
- riscv_info_t *r = riscv_info(target);
+ RISCV_INFO(r);
int result;
- if (r->is_halted == NULL) {
+ if (!r->is_halted) {
struct target_type *tt = get_target_type(target);
result = tt->halt(target);
} else {
{
RISCV_INFO(r);
- if (r->is_halted == NULL) {
+ if (!r->is_halted) {
struct target_type *tt = get_target_type(target);
return tt->halt(target);
}
int result = ERROR_OK;
if (target->smp) {
- for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+ struct target_list *tlist;
+ foreach_smp_target(tlist, target->smp_targets) {
struct target *t = tlist->target;
if (halt_prep(t) != ERROR_OK)
result = ERROR_FAIL;
}
- for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+ foreach_smp_target(tlist, target->smp_targets) {
struct target *t = tlist->target;
- riscv_info_t *i = riscv_info(t);
+ struct riscv_info *i = riscv_info(t);
if (i->prepped) {
if (halt_go(t) != ERROR_OK)
result = ERROR_FAIL;
}
}
- for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+ foreach_smp_target(tlist, target->smp_targets) {
struct target *t = tlist->target;
if (halt_finish(t) != ERROR_OK)
return ERROR_FAIL;
return ERROR_FAIL;
}
- if (riscv_rtos_enabled(target)) {
- if (r->rtos_hartid != -1) {
- LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid);
- target->rtos->current_threadid = r->rtos_hartid + 1;
- target->rtos->current_thread = r->rtos_hartid + 1;
- } else
- LOG_DEBUG("halt requested, but no known RTOS hartid");
- }
-
return result;
}
int riscv_resume_prep_all_harts(struct target *target)
{
RISCV_INFO(r);
- for (int i = 0; i < riscv_count_harts(target); ++i) {
- if (!riscv_hart_enabled(target, i))
- continue;
- LOG_DEBUG("prep hart %d", i);
- if (riscv_set_current_hartid(target, i) != ERROR_OK)
+ LOG_DEBUG("[%s] prep hart", target_name(target));
+ if (riscv_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_is_halted(target)) {
+ if (r->resume_prep(target) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_is_halted(target)) {
- if (r->resume_prep(target) != ERROR_OK)
- return ERROR_FAIL;
- } else {
- LOG_DEBUG(" hart %d requested resume, but was already resumed", i);
- }
+ } else {
+ LOG_DEBUG("[%s] hart requested resume, but was already resumed",
+ target_name(target));
}
- LOG_DEBUG("[%d] mark as prepped", target->coreid);
+ LOG_DEBUG("[%s] mark as prepped", target_name(target));
r->prepped = true;
return ERROR_OK;
if (riscv_enumerate_triggers(target) != ERROR_OK)
return ERROR_FAIL;
- int hartid = riscv_current_hartid(target);
if (r->manual_hwbp_set) {
/* Look at every trigger that may have been set. */
riscv_reg_t tselect;
if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
return ERROR_FAIL;
- for (unsigned t = 0; t < r->trigger_count[hartid]; t++) {
+ for (unsigned int t = 0; t < r->trigger_count; t++) {
if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
return ERROR_FAIL;
riscv_reg_t tdata1;
struct watchpoint *watchpoint = target->watchpoints;
int i = 0;
while (watchpoint) {
- LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set);
- state[i] = watchpoint->set;
- if (watchpoint->set) {
+ LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->is_set);
+ state[i] = watchpoint->is_set;
+ if (watchpoint->is_set) {
if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK)
return ERROR_FAIL;
}
{
RISCV_INFO(r);
- int hartid = riscv_current_hartid(target);
-
if (r->manual_hwbp_set) {
/* Look at every trigger that may have been set. */
riscv_reg_t tselect;
if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
return ERROR_FAIL;
- for (unsigned t = 0; t < r->trigger_count[hartid]; t++) {
+ for (unsigned int t = 0; t < r->trigger_count; t++) {
if (state[t] != 0) {
if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
return ERROR_FAIL;
static int resume_go(struct target *target, int current,
target_addr_t address, int handle_breakpoints, int debug_execution)
{
- riscv_info_t *r = riscv_info(target);
+ RISCV_INFO(r);
int result;
- if (r->is_halted == NULL) {
+ if (!r->is_halted) {
struct target_type *tt = get_target_type(target);
result = tt->resume(target, current, address, handle_breakpoints,
debug_execution);
LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
int result = ERROR_OK;
if (target->smp && !single_hart) {
- for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+ struct target_list *tlist;
+ foreach_smp_target_direction(resume_order == RO_NORMAL,
+ tlist, target->smp_targets) {
struct target *t = tlist->target;
if (resume_prep(t, current, address, handle_breakpoints,
debug_execution) != ERROR_OK)
result = ERROR_FAIL;
}
- for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+ foreach_smp_target_direction(resume_order == RO_NORMAL,
+ tlist, target->smp_targets) {
struct target *t = tlist->target;
- riscv_info_t *i = riscv_info(t);
+ struct riscv_info *i = riscv_info(t);
if (i->prepped) {
if (resume_go(t, current, address, handle_breakpoints,
debug_execution) != ERROR_OK)
}
}
- for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+ foreach_smp_target_direction(resume_order == RO_NORMAL,
+ tlist, target->smp_targets) {
struct target *t = tlist->target;
if (resume_finish(t) != ERROR_OK)
return ERROR_FAIL;
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) {
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) {
int mode;
uint64_t ppn_value;
target_addr_t table_address;
- virt2phys_info_t *info;
+ const virt2phys_info_t *info;
uint64_t pte = 0;
int i;
- if (riscv_rtos_enabled(target))
- riscv_set_current_hartid(target, target->rtos->current_thread - 1);
-
int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP);
if (result != ERROR_OK)
return result;
return tt->write_memory(target, address, size, count, buffer);
}
+const char *riscv_get_gdb_arch(struct target *target)
+{
+ switch (riscv_xlen(target)) {
+ case 32:
+ return "riscv:rv32";
+ case 64:
+ return "riscv:rv64";
+ }
+ LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target));
+ return NULL;
+}
+
static int riscv_get_gdb_reg_list_internal(struct target *target,
struct reg **reg_list[], int *reg_list_size,
enum target_register_class reg_class, bool read)
{
RISCV_INFO(r);
- LOG_DEBUG("rtos_hartid=%d, current_hartid=%d, reg_class=%d, read=%d",
- r->rtos_hartid, r->current_hartid, reg_class, read);
+ LOG_DEBUG("[%s] {%d} reg_class=%d, read=%d",
+ target_name(target), r->current_hartid, reg_class, read);
if (!target->reg_cache) {
LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
struct reg_param *reg_params, target_addr_t entry_point,
target_addr_t exit_point, int timeout_ms, void *arch_info)
{
- riscv_info_t *info = (riscv_info_t *) target->arch_info;
- int hartid = riscv_current_hartid(target);
+ RISCV_INFO(info);
if (num_mem_params > 0) {
LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
}
/* Save registers */
- struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", 1);
+ struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", true);
if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK)
return ERROR_FAIL;
uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
uint64_t saved_regs[32];
for (int i = 0; i < num_reg_params; i++) {
LOG_DEBUG("save %s", reg_params[i].reg_name);
- struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
+ struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
if (!r) {
LOG_ERROR("Couldn't find register named '%s'", reg_params[i].reg_name);
return ERROR_FAIL;
LOG_DEBUG("Disabling Interrupts");
struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
- "mstatus", 1);
+ "mstatus", true);
if (!reg_mstatus) {
LOG_ERROR("Couldn't find mstatus!");
return ERROR_FAIL;
reg_mstatus->type->get(reg_mstatus);
current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
- buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus,
+ buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus,
ie_mask, 0));
reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
GDB_REGNO_PC,
GDB_REGNO_MSTATUS, GDB_REGNO_MEPC, GDB_REGNO_MCAUSE,
};
- for (unsigned i = 0; i < DIM(regnums); i++) {
+ for (unsigned i = 0; i < ARRAY_SIZE(regnums); i++) {
enum gdb_regno regno = regnums[i];
riscv_reg_t reg_value;
if (riscv_get_register(target, ®_value, regno) != ERROR_OK)
}
/* The current hart id might have been changed in poll(). */
- if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+ if (riscv_select_current_hart(target) != ERROR_OK)
return ERROR_FAIL;
if (reg_pc->type->get(reg_pc) != ERROR_OK)
/* Restore Interrupts */
LOG_DEBUG("Restoring Interrupts");
- buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus);
+ buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus);
reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
/* Restore registers */
uint8_t buf[8] = { 0 };
- buf_set_u64(buf, 0, info->xlen[0], saved_pc);
+ buf_set_u64(buf, 0, info->xlen, saved_pc);
if (reg_pc->type->set(reg_pc, buf) != ERROR_OK)
return ERROR_FAIL;
for (int i = 0; i < num_reg_params; i++) {
if (reg_params[i].direction == PARAM_IN ||
reg_params[i].direction == PARAM_IN_OUT) {
- struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
+ struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
if (r->type->get(r) != ERROR_OK) {
LOG_ERROR("get(%s) failed", r->name);
return ERROR_FAIL;
buf_cpy(r->value, reg_params[i].value, reg_params[i].size);
}
LOG_DEBUG("restore %s", reg_params[i].reg_name);
- struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0);
- buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]);
+ struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
+ buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]);
if (r->type->set(r, buf) != ERROR_OK) {
LOG_ERROR("set(%s) failed", r->name);
return ERROR_FAIL;
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"
+#include "../../../contrib/loaders/checksum/riscv32_crc.inc"
};
static const uint8_t riscv64_crc_code[] = {
-#include "../../contrib/loaders/checksum/riscv64_crc.inc"
+#include "../../../contrib/loaders/checksum/riscv64_crc.inc"
};
static const uint8_t *crc_code;
return ERROR_OK;
}
-/*** OpenOCD Interface ***/
-int riscv_openocd_poll(struct target *target)
+int sample_memory(struct target *target)
{
- LOG_DEBUG("polling all harts");
- int halted_hart = -1;
- if (riscv_rtos_enabled(target)) {
- /* Check every hart for an event. */
- for (int i = 0; i < riscv_count_harts(target); ++i) {
- enum riscv_poll_hart out = riscv_poll_hart(target, i);
- switch (out) {
- case RPH_NO_CHANGE:
- case RPH_DISCOVERED_RUNNING:
- continue;
- case RPH_DISCOVERED_HALTED:
- halted_hart = i;
- break;
- case RPH_ERROR:
- return ERROR_FAIL;
+ RISCV_INFO(r);
+
+ if (!r->sample_buf.buf || !r->sample_config.enabled)
+ return ERROR_OK;
+
+ LOG_DEBUG("buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size);
+
+ uint64_t start = timeval_ms();
+ riscv_sample_buf_maybe_add_timestamp(target, true);
+ int result = ERROR_OK;
+ if (r->sample_memory) {
+ result = r->sample_memory(target, &r->sample_buf, &r->sample_config,
+ start + TARGET_DEFAULT_POLLING_INTERVAL);
+ if (result != ERROR_NOT_IMPLEMENTED)
+ goto exit;
+ }
+
+ /* Default slow path. */
+ while (timeval_ms() - start < TARGET_DEFAULT_POLLING_INTERVAL) {
+ for (unsigned int i = 0; i < ARRAY_SIZE(r->sample_config.bucket); i++) {
+ if (r->sample_config.bucket[i].enabled &&
+ r->sample_buf.used + 1 + r->sample_config.bucket[i].size_bytes < r->sample_buf.size) {
+ assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE);
+ r->sample_buf.buf[r->sample_buf.used] = i;
+ result = riscv_read_phys_memory(
+ target, r->sample_config.bucket[i].address,
+ r->sample_config.bucket[i].size_bytes, 1,
+ r->sample_buf.buf + r->sample_buf.used + 1);
+ if (result == ERROR_OK)
+ r->sample_buf.used += 1 + r->sample_config.bucket[i].size_bytes;
+ else
+ goto exit;
}
}
- if (halted_hart == -1) {
- LOG_DEBUG(" no harts just halted, target->state=%d", target->state);
- return ERROR_OK;
- }
- LOG_DEBUG(" hart %d halted", halted_hart);
-
- target->state = TARGET_HALTED;
- enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart);
- if (set_debug_reason(target, halt_reason) != ERROR_OK)
- return ERROR_FAIL;
+ }
- target->rtos->current_threadid = halted_hart + 1;
- target->rtos->current_thread = halted_hart + 1;
- riscv_set_rtos_hartid(target, halted_hart);
+exit:
+ riscv_sample_buf_maybe_add_timestamp(target, false);
+ if (result != ERROR_OK) {
+ LOG_INFO("Turning off memory sampling because it failed.");
+ r->sample_config.enabled = false;
+ }
+ return result;
+}
- /* If we're here then at least one hart triggered. That means we want
- * to go and halt _every_ hart (configured with -rtos riscv) in the
- * system, as that's the invariant we hold here. Some harts might have
- * already halted (as we're either in single-step mode or they also
- * triggered a breakpoint), so don't attempt to halt those harts.
- * riscv_halt() will do all that for us. */
- riscv_halt(target);
+/*** OpenOCD Interface ***/
+int riscv_openocd_poll(struct target *target)
+{
+ LOG_DEBUG("polling all harts");
+ int halted_hart = -1;
- } else if (target->smp) {
+ if (target->smp) {
unsigned halts_discovered = 0;
- unsigned total_targets = 0;
unsigned should_remain_halted = 0;
unsigned should_resume = 0;
- unsigned i = 0;
- for (struct target_list *list = target->head; list != NULL;
- list = list->next, i++) {
- total_targets++;
+ struct target_list *list;
+ foreach_smp_target(list, target->smp_targets) {
struct target *t = list->target;
- riscv_info_t *r = riscv_info(t);
+ struct riscv_info *r = riscv_info(t);
enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid);
switch (out) {
case RPH_NO_CHANGE:
if (halt_reason == RISCV_HALT_BREAKPOINT) {
int retval;
switch (riscv_semihosting(t, &retval)) {
- case SEMI_NONE:
- case SEMI_WAITING:
+ case SEMIHOSTING_NONE:
+ case SEMIHOSTING_WAITING:
/* This hart should remain halted. */
should_remain_halted++;
break;
- case SEMI_HANDLED:
+ case SEMIHOSTING_HANDLED:
/* This hart should be resumed, along with any other
* harts that halted due to haltgroups. */
should_resume++;
break;
- case SEMI_ERROR:
+ case SEMIHOSTING_ERROR:
return retval;
}
} else if (halt_reason != RISCV_HALT_GROUP) {
LOG_DEBUG("resume all");
riscv_resume(target, true, 0, 0, 0, false);
}
+
+ /* Sample memory if any target is running. */
+ foreach_smp_target(list, target->smp_targets) {
+ struct target *t = list->target;
+ if (t->state == TARGET_RUNNING) {
+ sample_memory(target);
+ break;
+ }
+ }
+
return ERROR_OK;
} else {
enum riscv_poll_hart out = riscv_poll_hart(target,
riscv_current_hartid(target));
- if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING)
+ if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) {
+ if (target->state == TARGET_RUNNING)
+ sample_memory(target);
return ERROR_OK;
- else if (out == RPH_ERROR)
+ } else if (out == RPH_ERROR) {
return ERROR_FAIL;
+ }
halted_hart = riscv_current_hartid(target);
LOG_DEBUG(" hart %d halted", halted_hart);
if (target->debug_reason == DBG_REASON_BREAKPOINT) {
int retval;
switch (riscv_semihosting(target, &retval)) {
- case SEMI_NONE:
- case SEMI_WAITING:
+ case SEMIHOSTING_NONE:
+ case SEMIHOSTING_WAITING:
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
break;
- case SEMI_HANDLED:
+ case SEMIHOSTING_HANDLED:
if (riscv_resume(target, true, 0, 0, 0, false) != ERROR_OK)
return ERROR_FAIL;
break;
- case SEMI_ERROR:
+ case SEMIHOSTING_ERROR:
return retval;
}
} else {
return ERROR_OK;
}
-COMMAND_HANDLER(riscv_test_compliance) {
-
+COMMAND_HANDLER(riscv_set_prefer_sba)
+{
struct target *target = get_current_target(CMD_CTX);
-
RISCV_INFO(r);
-
- if (CMD_ARGC > 0) {
- LOG_ERROR("Command does not take any parameters.");
+ bool prefer_sba;
+ LOG_WARNING("`riscv set_prefer_sba` is deprecated. Please use `riscv set_mem_access` instead.");
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("Command takes exactly 1 parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
-
- if (r->test_compliance) {
- return r->test_compliance(target);
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], prefer_sba);
+ if (prefer_sba) {
+ /* Use system bus with highest priority */
+ r->mem_access_methods[0] = RISCV_MEM_ACCESS_SYSBUS;
+ r->mem_access_methods[1] = RISCV_MEM_ACCESS_PROGBUF;
+ r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
} else {
- LOG_ERROR("This target does not support this command (may implement an older version of the spec).");
- return ERROR_FAIL;
+ /* Use progbuf with highest priority */
+ r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF;
+ r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS;
+ r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
}
+
+ /* Reset warning flags */
+ r->mem_access_progbuf_warn = true;
+ r->mem_access_sysbus_warn = true;
+ r->mem_access_abstract_warn = true;
+
+ return ERROR_OK;
}
-COMMAND_HANDLER(riscv_set_prefer_sba)
+COMMAND_HANDLER(riscv_set_mem_access)
{
- if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+ int progbuf_cnt = 0;
+ int sysbus_cnt = 0;
+ int abstract_cnt = 0;
+
+ if (CMD_ARGC < 1 || CMD_ARGC > RISCV_NUM_MEM_ACCESS_METHODS) {
+ LOG_ERROR("Command takes 1 to %d parameters", RISCV_NUM_MEM_ACCESS_METHODS);
return ERROR_COMMAND_SYNTAX_ERROR;
}
- COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_prefer_sba);
+
+ /* Check argument validity */
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ if (strcmp("progbuf", CMD_ARGV[i]) == 0) {
+ progbuf_cnt++;
+ } else if (strcmp("sysbus", CMD_ARGV[i]) == 0) {
+ sysbus_cnt++;
+ } else if (strcmp("abstract", CMD_ARGV[i]) == 0) {
+ abstract_cnt++;
+ } else {
+ LOG_ERROR("Unknown argument '%s'. "
+ "Must be one of: 'progbuf', 'sysbus' or 'abstract'.", CMD_ARGV[i]);
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ }
+ if (progbuf_cnt > 1 || sysbus_cnt > 1 || abstract_cnt > 1) {
+ LOG_ERROR("Syntax error - duplicate arguments to `riscv set_mem_access`.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ /* Args are valid, store them */
+ for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++)
+ r->mem_access_methods[i] = RISCV_MEM_ACCESS_UNSPECIFIED;
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ if (strcmp("progbuf", CMD_ARGV[i]) == 0)
+ r->mem_access_methods[i] = RISCV_MEM_ACCESS_PROGBUF;
+ else if (strcmp("sysbus", CMD_ARGV[i]) == 0)
+ r->mem_access_methods[i] = RISCV_MEM_ACCESS_SYSBUS;
+ else if (strcmp("abstract", CMD_ARGV[i]) == 0)
+ r->mem_access_methods[i] = RISCV_MEM_ACCESS_ABSTRACT;
+ }
+
+ /* Reset warning flags */
+ r->mem_access_progbuf_warn = true;
+ r->mem_access_sysbus_warn = true;
+ r->mem_access_abstract_warn = true;
+
return ERROR_OK;
}
return ERROR_OK;
}
-void parse_error(const char *string, char c, unsigned position)
+int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val)
{
- char buf[position+2];
- for (unsigned i = 0; i < position; i++)
- buf[i] = ' ';
- buf[position] = '^';
- buf[position + 1] = 0;
-
- LOG_ERROR("Parse error at character %c in:", c);
- LOG_ERROR("%s", string);
- LOG_ERROR("%s", buf);
-}
+ char *args = strdup(tcl_arg);
+ if (!args)
+ return ERROR_FAIL;
-int parse_ranges(range_t **ranges, const char **argv)
-{
- for (unsigned pass = 0; pass < 2; pass++) {
- unsigned range = 0;
+ /* For backward compatibility, allow multiple parameters within one TCL argument, separated by ',' */
+ char *arg = strtok(args, ",");
+ while (arg) {
unsigned low = 0;
- bool parse_low = true;
unsigned high = 0;
- for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) {
- char c = argv[0][i];
- if (isspace(c)) {
- /* Ignore whitespace. */
- continue;
+ char *name = NULL;
+
+ char *dash = strchr(arg, '-');
+ char *equals = strchr(arg, '=');
+ unsigned int pos;
+
+ if (!dash && !equals) {
+ /* Expecting single register number. */
+ if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
+ LOG_ERROR("Failed to parse single register number from '%s'.", arg);
+ free(args);
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ } else if (dash && !equals) {
+ /* Expecting register range - two numbers separated by a dash: ##-## */
+ *dash = 0;
+ dash++;
+ if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
+ LOG_ERROR("Failed to parse single register number from '%s'.", arg);
+ free(args);
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ if (sscanf(dash, "%u%n", &high, &pos) != 1 || pos != strlen(dash)) {
+ LOG_ERROR("Failed to parse single register number from '%s'.", dash);
+ free(args);
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ if (high < low) {
+ LOG_ERROR("Incorrect range encountered [%u, %u].", low, high);
+ free(args);
+ return ERROR_FAIL;
+ }
+ } else if (!dash && equals) {
+ /* Expecting single register number with textual name specified: ##=name */
+ *equals = 0;
+ equals++;
+ if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
+ LOG_ERROR("Failed to parse single register number from '%s'.", arg);
+ free(args);
+ return ERROR_COMMAND_SYNTAX_ERROR;
}
- if (parse_low) {
- if (isdigit(c)) {
- low *= 10;
- low += c - '0';
- } else if (c == '-') {
- parse_low = false;
- } else if (c == ',' || c == 0) {
- if (pass == 1) {
- (*ranges)[range].low = low;
- (*ranges)[range].high = low;
- }
- low = 0;
- range++;
- } else {
- parse_error(argv[0], c, i);
- return ERROR_COMMAND_SYNTAX_ERROR;
- }
+ name = calloc(1, strlen(equals) + strlen(reg_type) + 2);
+ if (!name) {
+ LOG_ERROR("Failed to allocate register name.");
+ free(args);
+ return ERROR_FAIL;
+ }
- } else {
- if (isdigit(c)) {
- high *= 10;
- high += c - '0';
- } else if (c == ',' || c == 0) {
- parse_low = true;
- if (pass == 1) {
- (*ranges)[range].low = low;
- (*ranges)[range].high = high;
- }
- low = 0;
- high = 0;
- range++;
- } else {
- parse_error(argv[0], c, i);
- return ERROR_COMMAND_SYNTAX_ERROR;
- }
+ /* Register prefix: "csr_" or "custom_" */
+ strcpy(name, reg_type);
+ name[strlen(reg_type)] = '_';
+
+ if (sscanf(equals, "%[_a-zA-Z0-9]%n", name + strlen(reg_type) + 1, &pos) != 1 || pos != strlen(equals)) {
+ LOG_ERROR("Failed to parse register name from '%s'.", equals);
+ free(args);
+ free(name);
+ return ERROR_COMMAND_SYNTAX_ERROR;
}
+ } else {
+ LOG_ERROR("Invalid argument '%s'.", arg);
+ free(args);
+ return ERROR_COMMAND_SYNTAX_ERROR;
}
- if (pass == 0) {
- free(*ranges);
- *ranges = calloc(range + 2, sizeof(range_t));
- if (!*ranges)
+ high = high > low ? high : low;
+
+ if (high > max_val) {
+ LOG_ERROR("Cannot expose %s register number %u, maximum allowed value is %u.", reg_type, high, max_val);
+ free(name);
+ free(args);
+ return ERROR_FAIL;
+ }
+
+ /* Check for overlap, name uniqueness. */
+ range_list_t *entry;
+ list_for_each_entry(entry, ranges, list) {
+ if ((entry->low <= high) && (low <= entry->high)) {
+ if (low == high)
+ LOG_WARNING("Duplicate %s register number - "
+ "Register %u has already been exposed previously", reg_type, low);
+ else
+ LOG_WARNING("Overlapping register ranges - Register range starting from %u overlaps "
+ "with already exposed register/range at %u.", low, entry->low);
+ }
+
+ if (entry->name && name && (strcasecmp(entry->name, name) == 0)) {
+ LOG_ERROR("Duplicate register name \"%s\" found.", name);
+ free(name);
+ free(args);
return ERROR_FAIL;
- } else {
- (*ranges)[range].low = 1;
- (*ranges)[range].high = 0;
+ }
}
+
+ range_list_t *range = calloc(1, sizeof(range_list_t));
+ if (!range) {
+ LOG_ERROR("Failed to allocate range list.");
+ free(name);
+ free(args);
+ return ERROR_FAIL;
+ }
+
+ range->low = low;
+ range->high = high;
+ range->name = name;
+ list_add(&range->list, ranges);
+
+ arg = strtok(NULL, ",");
}
+ free(args);
return ERROR_OK;
}
COMMAND_HANDLER(riscv_set_expose_csrs)
{
- if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
+ if (CMD_ARGC == 0) {
+ LOG_ERROR("Command expects parameters");
return ERROR_COMMAND_SYNTAX_ERROR;
}
- return parse_ranges(&expose_csr, CMD_ARGV);
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(info);
+ int ret = ERROR_OK;
+
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ ret = parse_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff);
+ if (ret != ERROR_OK)
+ break;
+ }
+
+ return ret;
}
COMMAND_HANDLER(riscv_set_expose_custom)
{
- if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
+ if (CMD_ARGC == 0) {
+ LOG_ERROR("Command expects parameters");
return ERROR_COMMAND_SYNTAX_ERROR;
}
- return parse_ranges(&expose_custom, CMD_ARGV);
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(info);
+ int ret = ERROR_OK;
+
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ ret = parse_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff);
+ if (ret != ERROR_OK)
+ break;
+ }
+
+ return ret;
}
COMMAND_HANDLER(riscv_authdata_read)
{
- if (CMD_ARGC != 0) {
- LOG_ERROR("Command takes no parameters");
+ unsigned int index = 0;
+ if (CMD_ARGC == 0) {
+ /* nop */
+ } else if (CMD_ARGC == 1) {
+ COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index);
+ } else {
+ LOG_ERROR("Command takes at most one parameter");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (r->authdata_read) {
uint32_t value;
- if (r->authdata_read(target, &value) != ERROR_OK)
+ if (r->authdata_read(target, &value, index) != ERROR_OK)
return ERROR_FAIL;
command_print_sameline(CMD, "0x%08" PRIx32, value);
return ERROR_OK;
COMMAND_HANDLER(riscv_authdata_write)
{
- if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 argument");
+ uint32_t value;
+ unsigned int index = 0;
+
+ if (CMD_ARGC == 0) {
+ /* nop */
+ } else if (CMD_ARGC == 1) {
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
+ } else if (CMD_ARGC == 2) {
+ COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+ } else {
+ LOG_ERROR("Command takes at most 2 arguments");
return ERROR_COMMAND_SYNTAX_ERROR;
}
struct target *target = get_current_target(CMD_CTX);
RISCV_INFO(r);
- uint32_t value;
- COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
-
if (r->authdata_write) {
- return r->authdata_write(target, value);
+ return r->authdata_write(target, value, index);
} else {
LOG_ERROR("authdata_write is not implemented for this target.");
return ERROR_FAIL;
return ERROR_OK;
}
+COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
+ unsigned int value)
+{
+ char full_key[80];
+ snprintf(full_key, sizeof(full_key), "%s.%s", section, key);
+ command_print(CMD, "%-21s %3d", full_key, value);
+ return 0;
+}
+
+COMMAND_HANDLER(handle_info)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ /* This output format can be fed directly into TCL's "array set". */
+
+ riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target));
+ riscv_enumerate_triggers(target);
+ riscv_print_info_line(CMD, "hart", "trigger_count",
+ r->trigger_count);
+
+ if (r->print_info)
+ return CALL_COMMAND_HANDLER(r->print_info, target);
+
+ return 0;
+}
+
static const struct command_registration riscv_exec_command_handlers[] = {
{
- .name = "test_compliance",
- .handler = riscv_test_compliance,
- .usage = "",
+ .name = "info",
+ .handler = handle_info,
.mode = COMMAND_EXEC,
- .help = "Runs a basic compliance test suite against the RISC-V Debug Spec."
+ .usage = "",
+ .help = "Displays some information OpenOCD detected about the target."
},
{
.name = "set_command_timeout_sec",
.help = "When on, prefer to use System Bus Access to access memory. "
"When off (default), prefer to use the Program Buffer to access memory."
},
+ {
+ .name = "set_mem_access",
+ .handler = riscv_set_mem_access,
+ .mode = COMMAND_ANY,
+ .usage = "method1 [method2] [method3]",
+ .help = "Set which memory access methods shall be used and in which order "
+ "of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'."
+ },
{
.name = "set_enable_virtual",
.handler = riscv_set_enable_virtual,
{
.name = "expose_csrs",
.handler = riscv_set_expose_csrs,
- .mode = COMMAND_ANY,
- .usage = "n0[-m0][,n1[-m1]]...",
+ .mode = COMMAND_CONFIG,
+ .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...",
.help = "Configure a list of inclusive ranges for CSRs to expose in "
"addition to the standard ones. This must be executed before "
"`init`."
{
.name = "expose_custom",
.handler = riscv_set_expose_custom,
- .mode = COMMAND_ANY,
- .usage = "n0[-m0][,n1[-m1]]...",
+ .mode = COMMAND_CONFIG,
+ .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...",
.help = "Configure a list of inclusive ranges for custom registers to "
"expose. custom0 is accessed as abstract register number 0xc000, "
"etc. This must be executed before `init`."
{
.name = "authdata_read",
.handler = riscv_authdata_read,
- .usage = "",
+ .usage = "[index]",
.mode = COMMAND_ANY,
- .help = "Return the 32-bit value read from authdata."
+ .help = "Return the 32-bit value read from authdata or authdata0 "
+ "(index=0), or authdata1 (index=1)."
},
{
.name = "authdata_write",
.handler = riscv_authdata_write,
.mode = COMMAND_ANY,
- .usage = "value",
- .help = "Write the 32-bit value to authdata."
+ .usage = "[index] value",
+ .help = "Write the 32-bit value to authdata or authdata0 (index=0), "
+ "or authdata1 (index=1)."
},
{
.name = "dmi_read",
* protocol, then a command like `riscv semihosting enable` will make
* sense, but for now all semihosting commands are prefixed with `arm`.
*/
-extern const struct command_registration semihosting_common_handlers[];
const struct command_registration riscv_command_handlers[] = {
{
return riscv_xlen(target);
}
+static unsigned int riscv_data_bits(struct target *target)
+{
+ RISCV_INFO(r);
+ if (r->data_bits)
+ return r->data_bits(target);
+ return riscv_xlen(target);
+}
+
struct target_type riscv_target = {
.name = "riscv",
+ .target_create = riscv_create_target,
.init_target = riscv_init_target,
.deinit_target = riscv_deinit_target,
.examine = riscv_examine,
.mmu = riscv_mmu,
.virt2phys = riscv_virt2phys,
+ .get_gdb_arch = riscv_get_gdb_arch,
.get_gdb_reg_list = riscv_get_gdb_reg_list,
.get_gdb_reg_list_noread = riscv_get_gdb_reg_list_noread,
.commands = riscv_command_handlers,
.address_bits = riscv_xlen_nonconst,
+ .data_bits = riscv_data_bits
};
/*** RISC-V Interface ***/
-void riscv_info_init(struct target *target, riscv_info_t *r)
+void riscv_info_init(struct target *target, struct riscv_info *r)
{
memset(r, 0, sizeof(*r));
r->dtm_version = 1;
- r->registers_initialized = false;
r->current_hartid = target->coreid;
+ r->version_specific = NULL;
memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
- for (size_t h = 0; h < RISCV_MAX_HARTS; ++h)
- r->xlen[h] = -1;
+ r->xlen = -1;
+
+ r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF;
+ r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS;
+ r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
+
+ r->mem_access_progbuf_warn = true;
+ r->mem_access_sysbus_warn = true;
+ r->mem_access_abstract_warn = true;
+
+ INIT_LIST_HEAD(&r->expose_csr);
+ INIT_LIST_HEAD(&r->expose_custom);
}
static int riscv_resume_go_all_harts(struct target *target)
{
RISCV_INFO(r);
- /* Dummy variables to make mingw32-gcc happy. */
- int first = 0;
- int last = 1;
- int step = 1;
- switch (resume_order) {
- case RO_NORMAL:
- first = 0;
- last = riscv_count_harts(target) - 1;
- step = 1;
- break;
- case RO_REVERSED:
- first = riscv_count_harts(target) - 1;
- last = 0;
- step = -1;
- break;
- default:
- assert(0);
- }
-
- for (int i = first; i != last + step; i += step) {
- if (!riscv_hart_enabled(target, i))
- continue;
-
- LOG_DEBUG("resuming hart %d", i);
- if (riscv_set_current_hartid(target, i) != ERROR_OK)
+ LOG_DEBUG("[%s] resuming hart", target_name(target));
+ if (riscv_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_is_halted(target)) {
+ if (r->resume_go(target) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_is_halted(target)) {
- if (r->resume_go(target) != ERROR_OK)
- return ERROR_FAIL;
- } else {
- LOG_DEBUG(" hart %d requested resume, but was already resumed", i);
- }
+ } else {
+ LOG_DEBUG("[%s] hart requested resume, but was already resumed",
+ target_name(target));
}
riscv_invalidate_register_cache(target);
int riscv_step_rtos_hart(struct target *target)
{
RISCV_INFO(r);
- int hartid = r->current_hartid;
- if (riscv_rtos_enabled(target)) {
- hartid = r->rtos_hartid;
- if (hartid == -1) {
- LOG_DEBUG("GDB has asked me to step \"any\" thread, so I'm stepping hart 0.");
- hartid = 0;
- }
- }
- if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+ if (riscv_select_current_hart(target) != ERROR_OK)
return ERROR_FAIL;
- LOG_DEBUG("stepping hart %d", hartid);
+ LOG_DEBUG("[%s] stepping", target_name(target));
if (!riscv_is_halted(target)) {
LOG_ERROR("Hart isn't halted before single step!");
return ERROR_OK;
}
-bool riscv_supports_extension(struct target *target, int hartid, char letter)
+bool riscv_supports_extension(struct target *target, char letter)
{
RISCV_INFO(r);
unsigned num;
num = letter - 'A';
else
return false;
- return r->misa[hartid] & (1 << num);
+ return r->misa & BIT(num);
}
unsigned riscv_xlen(const struct target *target)
-{
- return riscv_xlen_of_hart(target, riscv_current_hartid(target));
-}
-
-int riscv_xlen_of_hart(const struct target *target, int hartid)
{
RISCV_INFO(r);
- assert(r->xlen[hartid] != -1);
- return r->xlen[hartid];
-}
-
-bool riscv_rtos_enabled(const struct target *target)
-{
- return false;
+ return r->xlen;
}
int riscv_set_current_hartid(struct target *target, int hartid)
int previous_hartid = riscv_current_hartid(target);
r->current_hartid = hartid;
- assert(riscv_hart_enabled(target, hartid));
LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
if (r->select_current_hart(target) != ERROR_OK)
return ERROR_FAIL;
- /* This might get called during init, in which case we shouldn't be
- * setting up the register cache. */
- if (target_was_examined(target) && riscv_rtos_enabled(target))
- riscv_invalidate_register_cache(target);
-
return ERROR_OK;
}
void riscv_invalidate_register_cache(struct target *target)
{
- RISCV_INFO(r);
-
LOG_DEBUG("[%d]", target->coreid);
register_cache_invalidate(target->reg_cache);
for (size_t i = 0; i < target->reg_cache->num_regs; ++i) {
struct reg *reg = &target->reg_cache->reg_list[i];
reg->valid = false;
}
-
- r->registers_initialized = true;
}
int riscv_current_hartid(const struct target *target)
return r->current_hartid;
}
-void riscv_set_all_rtos_harts(struct target *target)
-{
- RISCV_INFO(r);
- r->rtos_hartid = -1;
-}
-
-void riscv_set_rtos_hartid(struct target *target, int hartid)
-{
- LOG_DEBUG("setting RTOS hartid %d", hartid);
- RISCV_INFO(r);
- r->rtos_hartid = hartid;
-}
-
int riscv_count_harts(struct target *target)
{
- if (target == NULL)
+ if (!target)
return 1;
RISCV_INFO(r);
- if (r == NULL || r->hart_count == NULL)
+ if (!r || !r->hart_count)
return 1;
return r->hart_count(target);
}
-bool riscv_has_register(struct target *target, int hartid, int regid)
-{
- return 1;
-}
-
/**
* If write is true:
* return true iff we are guaranteed that the register will contain exactly
(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31))
return true;
- /* Most CSRs won't change value on us, but we can't assume it about rbitrary
+ /* Most CSRs won't change value on us, but we can't assume it about arbitrary
* CSRs. */
switch (regno) {
case GDB_REGNO_DPC:
* This function is called when the debug user wants to change the value of a
* register. The new value may be cached, and may not be written until the hart
* is resumed. */
-int riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
-{
- return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
-}
-
-int riscv_set_register_on_hart(struct target *target, int hartid,
- enum gdb_regno regid, uint64_t value)
+int riscv_set_register(struct target *target, enum gdb_regno regid, riscv_reg_t value)
{
RISCV_INFO(r);
- LOG_DEBUG("{%d} %s <- %" PRIx64, hartid, gdb_regno_name(regid), value);
+ LOG_DEBUG("[%s] %s <- %" PRIx64, target_name(target), gdb_regno_name(regid), value);
assert(r->set_register);
+ keep_alive();
+
/* TODO: Hack to deal with gdb that thinks these registers still exist. */
if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 &&
- riscv_supports_extension(target, hartid, 'E'))
+ riscv_supports_extension(target, 'E'))
return ERROR_OK;
struct reg *reg = &target->reg_cache->reg_list[regid];
buf_set_u64(reg->value, 0, reg->size, value);
- int result = r->set_register(target, hartid, regid, value);
+ int result = r->set_register(target, regid, value);
if (result == ERROR_OK)
reg->valid = gdb_regno_cacheable(regid, true);
else
reg->valid = false;
- LOG_DEBUG("[%s]{%d} wrote 0x%" PRIx64 " to %s valid=%d",
- target_name(target), hartid, value, reg->name, reg->valid);
+ LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d",
+ target_name(target), value, reg->name, reg->valid);
return result;
}
int riscv_get_register(struct target *target, riscv_reg_t *value,
- enum gdb_regno r)
-{
- return riscv_get_register_on_hart(target, value,
- riscv_current_hartid(target), r);
-}
-
-int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
- int hartid, enum gdb_regno regid)
+ enum gdb_regno regid)
{
RISCV_INFO(r);
+ keep_alive();
+
struct reg *reg = &target->reg_cache->reg_list[regid];
if (!reg->exist) {
- LOG_DEBUG("[%s]{%d} %s does not exist.",
- target_name(target), hartid, gdb_regno_name(regid));
+ LOG_DEBUG("[%s] %s does not exist.",
+ target_name(target), gdb_regno_name(regid));
return ERROR_FAIL;
}
- if (reg && reg->valid && hartid == riscv_current_hartid(target)) {
+ if (reg && reg->valid) {
*value = buf_get_u64(reg->value, 0, reg->size);
- LOG_DEBUG("{%d} %s: %" PRIx64 " (cached)", hartid,
+ LOG_DEBUG("[%s] %s: %" PRIx64 " (cached)", target_name(target),
gdb_regno_name(regid), *value);
return ERROR_OK;
}
/* TODO: Hack to deal with gdb that thinks these registers still exist. */
if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 &&
- riscv_supports_extension(target, hartid, 'E')) {
+ riscv_supports_extension(target, 'E')) {
*value = 0;
return ERROR_OK;
}
- int result = r->get_register(target, value, hartid, regid);
+ int result = r->get_register(target, value, regid);
if (result == ERROR_OK)
reg->valid = gdb_regno_cacheable(regid, false);
- LOG_DEBUG("{%d} %s: %" PRIx64, hartid, gdb_regno_name(regid), *value);
+ LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target),
+ gdb_regno_name(regid), *value);
return result;
}
size_t riscv_debug_buffer_size(struct target *target)
{
RISCV_INFO(r);
- return r->debug_buffer_size[riscv_current_hartid(target)];
+ return r->debug_buffer_size;
}
int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
return r->dmi_write_u64_bits(target);
}
-bool riscv_hart_enabled(struct target *target, int hartid)
-{
- /* FIXME: Add a hart mask to the RTOS. */
- if (riscv_rtos_enabled(target))
- return hartid < riscv_count_harts(target);
-
- return hartid == target->coreid;
-}
-
/**
* Count triggers, and initialize trigger_count for each hart.
* trigger_count is initialized even if this function fails to discover
r->triggers_enumerated = true; /* At the very least we tried. */
- for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
- if (!riscv_hart_enabled(target, hartid))
- continue;
+ riscv_reg_t tselect;
+ int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+ /* If tselect is not readable, the trigger module is likely not
+ * implemented. There are no triggers to enumerate then and no error
+ * should be thrown. */
+ if (result != ERROR_OK) {
+ LOG_DEBUG("[%s] Cannot access tselect register. "
+ "Assuming that triggers are not implemented.", target_name(target));
+ r->trigger_count = 0;
+ return ERROR_OK;
+ }
- riscv_reg_t tselect;
- int result = riscv_get_register_on_hart(target, &tselect, hartid,
- GDB_REGNO_TSELECT);
+ for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
+ r->trigger_count = t;
+
+ /* If we can't write tselect, then this hart does not support triggers. */
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
+ break;
+ uint64_t tselect_rb;
+ result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT);
+ if (result != ERROR_OK)
+ return result;
+ /* Mask off the top bit, which is used as tdrmode in old
+ * implementations. */
+ tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
+ if (tselect_rb != t)
+ break;
+ uint64_t tdata1;
+ result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
if (result != ERROR_OK)
return result;
- for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
- r->trigger_count[hartid] = t;
-
- /* If we can't write tselect, then this hart does not support triggers. */
- if (riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t) != ERROR_OK)
+ int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+ if (type == 0)
+ break;
+ switch (type) {
+ case 1:
+ /* On these older cores we don't support software using
+ * triggers. */
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
break;
- uint64_t tselect_rb;
- result = riscv_get_register_on_hart(target, &tselect_rb, hartid,
- GDB_REGNO_TSELECT);
- if (result != ERROR_OK)
- return result;
- /* Mask off the top bit, which is used as tdrmode in old
- * implementations. */
- tselect_rb &= ~(1ULL << (riscv_xlen(target)-1));
- if (tselect_rb != t)
+ case 2:
+ if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
break;
- uint64_t tdata1;
- result = riscv_get_register_on_hart(target, &tdata1, hartid,
- GDB_REGNO_TDATA1);
- if (result != ERROR_OK)
- return result;
-
- int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
- if (type == 0)
+ case 6:
+ if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
break;
- switch (type) {
- case 1:
- /* On these older cores we don't support software using
- * triggers. */
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
- break;
- case 2:
- if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
- break;
- }
}
+ }
- riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
+ riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
- LOG_INFO("[%d] Found %d triggers", hartid, r->trigger_count[hartid]);
- }
+ LOG_INFO("[%s] Found %d triggers", target_name(target), r->trigger_count);
return ERROR_OK;
}
}
reg->valid = gdb_regno_cacheable(reg->number, false);
char *str = buf_to_hex_str(reg->value, reg->size);
- LOG_DEBUG("[%d]{%d} read 0x%s from %s (valid=%d)", target->coreid,
- riscv_current_hartid(target), str, reg->name, reg->valid);
+ LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target),
+ str, reg->name, reg->valid);
free(str);
return ERROR_OK;
}
RISCV_INFO(r);
char *str = buf_to_hex_str(buf, reg->size);
- LOG_DEBUG("[%d]{%d} write 0x%s to %s (valid=%d)", target->coreid,
- riscv_current_hartid(target), str, reg->name, reg->valid);
+ LOG_DEBUG("[%s] write 0x%s to %s (valid=%d)", target_name(target),
+ str, reg->name, reg->valid);
free(str);
+ /* Exit early for writing x0, which on the hardware would be ignored, and we
+ * don't want to update our cache. */
+ if (reg->number == GDB_REGNO_ZERO)
+ return ERROR_OK;
+
memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
reg->valid = gdb_regno_cacheable(reg->number, true);
target->reg_cache->name = "RISC-V Registers";
target->reg_cache->num_regs = GDB_REGNO_COUNT;
- if (expose_custom) {
- for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) {
- for (unsigned number = expose_custom[i].low;
- number <= expose_custom[i].high;
- number++)
- target->reg_cache->num_regs++;
- }
+ if (!list_empty(&info->expose_custom)) {
+ range_list_t *entry;
+ list_for_each_entry(entry, &info->expose_custom, list)
+ target->reg_cache->num_regs += entry->high - entry->low + 1;
}
LOG_DEBUG("create register cache for %d registers",
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"
};
*/
info->vector_uint8.type = &type_uint8;
- info->vector_uint8.count = info->vlenb[hartid];
+ info->vector_uint8.count = info->vlenb;
info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint8_vector.id = "bytes";
info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
info->vector_uint16.type = &type_uint16;
- info->vector_uint16.count = info->vlenb[hartid] / 2;
+ info->vector_uint16.count = info->vlenb / 2;
info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint16_vector.id = "shorts";
info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
info->vector_uint32.type = &type_uint32;
- info->vector_uint32.count = info->vlenb[hartid] / 4;
+ info->vector_uint32.count = info->vlenb / 4;
info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint32_vector.id = "words";
info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
info->vector_uint64.type = &type_uint64;
- info->vector_uint64.count = info->vlenb[hartid] / 8;
+ info->vector_uint64.count = info->vlenb / 8;
info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint64_vector.id = "longs";
info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
info->vector_uint128.type = &type_uint128;
- info->vector_uint128.count = info->vlenb[hartid] / 16;
+ info->vector_uint128.count = info->vlenb / 16;
info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint128_vector.id = "quads";
info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->vector_fields[0].name = "b";
info->vector_fields[0].type = &info->type_uint8_vector;
- if (info->vlenb[hartid] >= 2) {
+ if (info->vlenb >= 2) {
info->vector_fields[0].next = info->vector_fields + 1;
info->vector_fields[1].name = "s";
info->vector_fields[1].type = &info->type_uint16_vector;
} else {
info->vector_fields[0].next = NULL;
}
- if (info->vlenb[hartid] >= 4) {
+ if (info->vlenb >= 4) {
info->vector_fields[1].next = info->vector_fields + 2;
info->vector_fields[2].name = "w";
info->vector_fields[2].type = &info->type_uint32_vector;
} else {
info->vector_fields[1].next = NULL;
}
- if (info->vlenb[hartid] >= 8) {
+ if (info->vlenb >= 8) {
info->vector_fields[2].next = info->vector_fields + 3;
info->vector_fields[3].name = "l";
info->vector_fields[3].type = &info->type_uint64_vector;
} else {
info->vector_fields[2].next = NULL;
}
- if (info->vlenb[hartid] >= 16) {
+ if (info->vlenb >= 16) {
info->vector_fields[3].next = info->vector_fields + 4;
info->vector_fields[4].name = "q";
info->vector_fields[4].type = &info->type_uint128_vector;
#undef DECLARE_CSR
};
/* encoding.h does not contain the registers in sorted order. */
- qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
+ qsort(csr_info, ARRAY_SIZE(csr_info), sizeof(*csr_info), cmp_csr_info);
unsigned csr_info_index = 0;
- unsigned custom_range_index = 0;
int custom_within_range = 0;
riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
* of other things to break in that case as well. */
if (number <= GDB_REGNO_XPR31) {
r->exist = number <= GDB_REGNO_XPR15 ||
- !riscv_supports_extension(target, hartid, 'E');
+ !riscv_supports_extension(target, 'E');
/* TODO: For now we fake that all GPRs exist because otherwise gdb
* doesn't work. */
r->exist = true;
r->feature = &feature_cpu;
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
r->caller_save = true;
- if (riscv_supports_extension(target, hartid, 'D')) {
+ if (riscv_supports_extension(target, 'D')) {
r->size = 64;
- if (riscv_supports_extension(target, hartid, 'F'))
+ if (riscv_supports_extension(target, 'F'))
r->reg_data_type = &type_ieee_single_double;
else
r->reg_data_type = &type_ieee_double;
- } else if (riscv_supports_extension(target, hartid, 'F')) {
+ } else if (riscv_supports_extension(target, 'F')) {
r->reg_data_type = &type_ieee_single;
r->size = 32;
} else {
unsigned csr_number = number - GDB_REGNO_CSR0;
while (csr_info[csr_info_index].number < csr_number &&
- csr_info_index < DIM(csr_info) - 1) {
+ csr_info_index < ARRAY_SIZE(csr_info) - 1) {
csr_info_index++;
}
if (csr_info[csr_info_index].number == csr_number) {
case CSR_FFLAGS:
case CSR_FRM:
case CSR_FCSR:
- r->exist = riscv_supports_extension(target, hartid, 'F');
+ r->exist = riscv_supports_extension(target, 'F');
r->group = "float";
r->feature = &feature_fpu;
break;
case CSR_SCAUSE:
case CSR_STVAL:
case CSR_SATP:
- r->exist = riscv_supports_extension(target, hartid, 'S');
+ r->exist = riscv_supports_extension(target, 'S');
break;
case CSR_MEDELEG:
case CSR_MIDELEG:
/* "In systems with only M-mode, or with both M-mode and
* U-mode but without U-mode trap support, the medeleg and
* mideleg registers should not exist." */
- r->exist = riscv_supports_extension(target, hartid, 'S') ||
- riscv_supports_extension(target, hartid, 'N');
+ r->exist = riscv_supports_extension(target, 'S') ||
+ riscv_supports_extension(target, 'N');
break;
case CSR_PMPCFG1:
case CSR_VL:
case CSR_VTYPE:
case CSR_VLENB:
- r->exist = riscv_supports_extension(target, hartid, 'V');
+ r->exist = riscv_supports_extension(target, 'V');
break;
}
- if (!r->exist && expose_csr) {
- for (unsigned i = 0; expose_csr[i].low <= expose_csr[i].high; i++) {
- if (csr_number >= expose_csr[i].low && csr_number <= expose_csr[i].high) {
- LOG_INFO("Exposing additional CSR %d", csr_number);
+ if (!r->exist && !list_empty(&info->expose_csr)) {
+ range_list_t *entry;
+ list_for_each_entry(entry, &info->expose_csr, list)
+ if ((entry->low <= csr_number) && (csr_number <= entry->high)) {
+ if (entry->name) {
+ *reg_name = 0;
+ r->name = entry->name;
+ }
+
+ LOG_DEBUG("Exposing additional CSR %d (name=%s)",
+ csr_number, entry->name ? entry->name : reg_name);
+
r->exist = true;
break;
}
- }
}
} else if (number == GDB_REGNO_PRIV) {
} else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
r->caller_save = false;
- r->exist = riscv_supports_extension(target, hartid, 'V') && info->vlenb[hartid];
- r->size = info->vlenb[hartid] * 8;
+ r->exist = riscv_supports_extension(target, 'V') && info->vlenb;
+ r->size = info->vlenb * 8;
sprintf(reg_name, "v%d", number - GDB_REGNO_V0);
r->group = "vector";
r->feature = &feature_vector;
} else if (number >= GDB_REGNO_COUNT) {
/* Custom registers. */
- assert(expose_custom);
+ assert(!list_empty(&info->expose_custom));
+
+ range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list);
- range_t *range = &expose_custom[custom_range_index];
- assert(range->low <= range->high);
unsigned custom_number = range->low + custom_within_range;
r->group = "custom";
((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number;
sprintf(reg_name, "custom%d", custom_number);
+ if (range->name) {
+ *reg_name = 0;
+ r->name = range->name;
+ }
+
+ LOG_DEBUG("Exposing additional custom register %d (name=%s)",
+ number, range->name ? range->name : reg_name);
+
custom_within_range++;
if (custom_within_range > range->high - range->low) {
custom_within_range = 0;
- custom_range_index++;
+ list_rotate_left(&info->expose_custom);
}
}
- if (reg_name[0])
+ if (reg_name[0]) {
r->name = reg_name;
- reg_name += strlen(reg_name) + 1;
- assert(reg_name < info->reg_names + target->reg_cache->num_regs *
- max_reg_name_len);
- r->value = info->reg_cache_values[number];
+ reg_name += strlen(reg_name) + 1;
+ assert(reg_name < info->reg_names + target->reg_cache->num_regs *
+ max_reg_name_len);
+ }
+ r->value = calloc(1, DIV_ROUND_UP(r->size, 8));
}
return ERROR_OK;