X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Ftarget%2Friscv%2Friscv.c;h=084939e116f3c8dc9002c87a97ac98b04416ae47;hp=8f1f398b3ea708b8319e1a3c1e06f2224a4a75c3;hb=0fb131c23a1cba9ea128db9c72fcd6479e508513;hpb=391782318723915bd259eadf9469251c13c8fa9c;ds=sidebyside diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 8f1f398b3e..084939e116 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -8,17 +8,19 @@ #include "config.h" #endif +#include +#include #include "target/target.h" #include "target/algorithm.h" #include "target/target_type.h" -#include "log.h" #include "jtag/jtag.h" #include "target/register.h" #include "target/breakpoints.h" -#include "helper/time_support.h" #include "riscv.h" #include "gdb_regs.h" #include "rtos/rtos.h" +#include "debug_defines.h" +#include #define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) #define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) @@ -125,10 +127,10 @@ struct scan_field select_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}; +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 @@ -202,7 +204,6 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC; /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC; -bool riscv_prefer_sba; bool riscv_enable_virt2phys = true; bool riscv_ebreakm = true; bool riscv_ebreaks = true; @@ -210,24 +211,12 @@ bool riscv_ebreaku = true; bool riscv_enable_virtual; -typedef struct { - uint16_t low, high; -} range_t; - -/* In addition to the ones in the standard spec, we'll also expose additional - * CSRs in this list. - * The list is either NULL, or a series of ranges (inclusive), terminated with - * 1,0. */ -range_t *expose_csr; -/* Same, but for custom registers. */ -range_t *expose_custom; - static enum { RO_NORMAL, RO_REVERSED } resume_order; -virt2phys_info_t sv32 = { +const virt2phys_info_t sv32 = { .name = "Sv32", .va_bits = 32, .level = 2, @@ -240,7 +229,7 @@ virt2phys_info_t sv32 = { .pa_ppn_mask = {0x3ff, 0xfff}, }; -virt2phys_info_t sv39 = { +const virt2phys_info_t sv39 = { .name = "Sv39", .va_bits = 39, .level = 3, @@ -253,7 +242,7 @@ virt2phys_info_t sv39 = { .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff}, }; -virt2phys_info_t sv48 = { +const virt2phys_info_t sv48 = { .name = "Sv48", .va_bits = 48, .level = 4, @@ -266,6 +255,22 @@ virt2phys_info_t sv48 = { .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) @@ -363,8 +368,6 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) return in; } - - static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) { struct scan_field field; @@ -419,15 +422,23 @@ static struct target_type *get_target_type(struct target *target) } } +static int riscv_create_target(struct target *target, Jim_Interp *interp) +{ + LOG_DEBUG("riscv_create_target()"); + target->arch_info = calloc(1, sizeof(riscv_info_t)); + if (!target->arch_info) { + LOG_ERROR("Failed to allocate RISC-V target structure."); + return ERROR_FAIL; + } + riscv_info_init(target, target->arch_info); + return ERROR_OK; +} + static int riscv_init_target(struct command_context *cmd_ctx, struct target *target) { LOG_DEBUG("riscv_init_target()"); - target->arch_info = calloc(1, sizeof(riscv_info_t)); - if (!target->arch_info) - return ERROR_FAIL; - riscv_info_t *info = (riscv_info_t *) target->arch_info; - riscv_info_init(target, info); + RISCV_INFO(info); info->cmd_ctx = cmd_ctx; select_dtmcontrol.num_bits = target->tap->ir_length; @@ -435,6 +446,12 @@ static int riscv_init_target(struct command_context *cmd_ctx, select_idcode.num_bits = target->tap->ir_length; if (bscan_tunnel_ir_width != 0) { + assert(target->tap->ir_length >= 6); + uint32_t ir_user4_raw = 0x23 << (target->tap->ir_length - 6); + ir_user4[0] = (uint8_t)ir_user4_raw; + ir_user4[1] = (uint8_t)(ir_user4_raw >>= 8); + ir_user4[2] = (uint8_t)(ir_user4_raw >>= 8); + ir_user4[3] = (uint8_t)(ir_user4_raw >>= 8); select_user4.num_bits = target->tap->ir_length; bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width; if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) @@ -468,16 +485,29 @@ static void riscv_free_registers(struct target *target) static void riscv_deinit_target(struct target *target) { LOG_DEBUG("riscv_deinit_target()"); + + riscv_info_t *info = target->arch_info; struct target_type *tt = get_target_type(target); - if (tt) { + + if (tt && info->version_specific) tt->deinit_target(target); - riscv_info_t *info = (riscv_info_t *) target->arch_info; - free(info->reg_names); - free(info); - } riscv_free_registers(target); + range_list_t *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) { + free(entry->name); + free(entry); + } + + 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; } @@ -494,7 +524,7 @@ static void trigger_from_breakpoint(struct trigger *trigger, trigger->unique_id = breakpoint->unique_id; } -static int maybe_add_trigger_t1(struct target *target, unsigned hartid, +static int maybe_add_trigger_t1(struct target *target, struct trigger *trigger, uint64_t tdata1) { RISCV_INFO(r); @@ -518,20 +548,19 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid, tdata1 = set_field(tdata1, bpcontrol_w, trigger->write); tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute); tdata1 = set_field(tdata1, bpcontrol_u, - !!(r->misa[hartid] & (1 << ('U' - 'A')))); + !!(r->misa & BIT('U' - 'A'))); tdata1 = set_field(tdata1, bpcontrol_s, - !!(r->misa[hartid] & (1 << ('S' - 'A')))); + !!(r->misa & BIT('S' - 'A'))); tdata1 = set_field(tdata1, bpcontrol_h, - !!(r->misa[hartid] & (1 << ('H' - 'A')))); + !!(r->misa & BIT('H' - 'A'))); tdata1 |= bpcontrol_m; tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */ tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */ - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1); + riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); riscv_reg_t tdata1_rb; - if (riscv_get_register_on_hart(target, &tdata1_rb, hartid, - GDB_REGNO_TDATA1) != ERROR_OK) + if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK) return ERROR_FAIL; LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb); @@ -539,16 +568,16 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid, LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%" PRIx64 " to tdata1 it contains 0x%" PRIx64, tdata1, tdata1_rb); - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); + riscv_set_register(target, GDB_REGNO_TDATA1, 0); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address); + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); return ERROR_OK; } -static int maybe_add_trigger_t2(struct target *target, unsigned hartid, +static int maybe_add_trigger_t2(struct target *target, struct trigger *trigger, uint64_t tdata1) { RISCV_INFO(r); @@ -565,11 +594,9 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, MCONTROL_ACTION_DEBUG_MODE); tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL); tdata1 |= MCONTROL_M; - if (r->misa[hartid] & (1 << ('H' - 'A'))) - tdata1 |= MCONTROL_H; - if (r->misa[hartid] & (1 << ('S' - 'A'))) + if (r->misa & (1 << ('S' - 'A'))) tdata1 |= MCONTROL_S; - if (r->misa[hartid] & (1 << ('U' - 'A'))) + if (r->misa & (1 << ('U' - 'A'))) tdata1 |= MCONTROL_U; if (trigger->execute) @@ -579,10 +606,10 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, if (trigger->write) tdata1 |= MCONTROL_STORE; - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1); + riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); uint64_t tdata1_rb; - int result = riscv_get_register_on_hart(target, &tdata1_rb, hartid, GDB_REGNO_TDATA1); + int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1); if (result != ERROR_OK) return result; LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb); @@ -591,77 +618,104 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%" PRIx64 " to tdata1 it contains 0x%" PRIx64, tdata1, tdata1_rb); - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); + riscv_set_register(target, GDB_REGNO_TDATA1, 0); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address); + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); return ERROR_OK; } -static int add_trigger(struct target *target, struct trigger *trigger) +static int maybe_add_trigger_t6(struct target *target, + struct trigger *trigger, uint64_t tdata1) { RISCV_INFO(r); - if (riscv_enumerate_triggers(target) != ERROR_OK) - return ERROR_FAIL; + /* tselect is already set */ + if (tdata1 & (CSR_MCONTROL6_EXECUTE | CSR_MCONTROL6_STORE | CSR_MCONTROL6_LOAD)) { + /* Trigger is already in use, presumably by user code. */ + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* 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; - /* 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. */ + if (trigger->execute) + tdata1 |= CSR_MCONTROL6_EXECUTE; + if (trigger->read) + tdata1 |= CSR_MCONTROL6_LOAD; + if (trigger->write) + tdata1 |= CSR_MCONTROL6_STORE; - /* Otherwise, we just set the trigger on the one hart this target deals - * with. */ + riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); - riscv_reg_t tselect[RISCV_MAX_HARTS]; + 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); - 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; + 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; } @@ -674,14 +728,9 @@ static int add_trigger(struct target *target, struct trigger *trigger) break; } - for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, - tselect[hartid]); - } + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); - if (i >= r->trigger_count[first_hart]) { + if (i >= r->trigger_count) { LOG_ERROR("Couldn't find an available hardware trigger."); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -689,6 +738,124 @@ static int add_trigger(struct target *target, struct trigger *trigger) return ERROR_OK; } +/** + * Write one memory item of given "size". Use memory access of given "access_size". + * Utilize read-modify-write, if needed. + * */ +static int write_by_given_size(struct target *target, target_addr_t address, + uint32_t size, uint8_t *buffer, uint32_t access_size) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8); + + if (access_size <= size && address % access_size == 0) + /* Can do the memory access directly without a helper buffer. */ + return target_write_memory(target, address, access_size, size / access_size, buffer); + + unsigned int offset_head = address % access_size; + unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2; + uint8_t helper_buf[n_blocks * access_size]; + + /* Read from memory */ + if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK) + return ERROR_FAIL; + + /* Modify and write back */ + memcpy(helper_buf + offset_head, buffer, size); + return target_write_memory(target, address - offset_head, access_size, n_blocks, helper_buf); +} + +/** + * Read one memory item of given "size". Use memory access of given "access_size". + * Read larger section of memory and pick out the required portion, if needed. + * */ +static int read_by_given_size(struct target *target, target_addr_t address, + uint32_t size, uint8_t *buffer, uint32_t access_size) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8); + + if (access_size <= size && address % access_size == 0) + /* Can do the memory access directly without a helper buffer. */ + return target_read_memory(target, address, access_size, size / access_size, buffer); + + unsigned int offset_head = address % access_size; + unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2; + uint8_t helper_buf[n_blocks * access_size]; + + /* Read from memory */ + if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK) + return ERROR_FAIL; + + /* Pick the requested portion from the buffer */ + memcpy(buffer, helper_buf + offset_head, size); + return ERROR_OK; +} + +/** + * Write one memory item using any memory access size that will work. + * Utilize read-modify-write, if needed. + * */ +int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + + /* Find access size that correspond to data size and the alignment. */ + unsigned int preferred_size = size; + while (address % preferred_size != 0) + preferred_size /= 2; + + /* First try the preferred (most natural) access size. */ + if (write_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK) + return ERROR_OK; + + /* On failure, try other access sizes. + Minimize the number of accesses by trying first the largest size. */ + for (unsigned int access_size = 8; access_size > 0; access_size /= 2) { + if (access_size == preferred_size) + /* Already tried this size. */ + continue; + + if (write_by_given_size(target, address, size, buffer, access_size) == ERROR_OK) + return ERROR_OK; + } + + /* No access attempt succeeded. */ + return ERROR_FAIL; +} + +/** + * Read one memory item using any memory access size that will work. + * Read larger section of memory and pick out the required portion, if needed. + * */ +int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + + /* Find access size that correspond to data size and the alignment. */ + unsigned int preferred_size = size; + while (address % preferred_size != 0) + preferred_size /= 2; + + /* First try the preferred (most natural) access size. */ + if (read_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK) + return ERROR_OK; + + /* On failure, try other access sizes. + Minimize the number of accesses by trying first the largest size. */ + for (unsigned int access_size = 8; access_size > 0; access_size /= 2) { + if (access_size == preferred_size) + /* Already tried this size. */ + continue; + + if (read_by_given_size(target, address, size, buffer, access_size) == ERROR_OK) + return ERROR_OK; + } + + /* No access attempt succeeded. */ + return ERROR_FAIL; +} + int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) { LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address); @@ -705,8 +872,9 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) return ERROR_FAIL; } - if (target_read_memory(target, breakpoint->address, 2, breakpoint->length / 2, - breakpoint->orig_instr) != ERROR_OK) { + /* Read the original instruction. */ + if (riscv_read_by_any_size( + target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR, breakpoint->address); return ERROR_FAIL; @@ -714,9 +882,8 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) uint8_t buff[4] = { 0 }; buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c()); - int const retval = target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2, buff); - - if (retval != ERROR_OK) { + /* Write the ebreak instruction. */ + if (riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) { LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); return ERROR_FAIL; @@ -744,40 +911,26 @@ static int remove_trigger(struct target *target, struct trigger *trigger) if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; - int first_hart = -1; - for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - if (first_hart < 0) { - first_hart = hartid; - break; - } - } - assert(first_hart >= 0); - unsigned int i; - for (i = 0; i < r->trigger_count[first_hart]; i++) { + for (i = 0; i < r->trigger_count; i++) { if (r->trigger_unique_id[i] == trigger->unique_id) break; } - if (i >= r->trigger_count[first_hart]) { + if (i >= r->trigger_count) { LOG_ERROR("Couldn't find the hardware resources used by hardware " "trigger."); return ERROR_FAIL; } LOG_DEBUG("[%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; @@ -787,8 +940,9 @@ int riscv_remove_breakpoint(struct target *target, struct breakpoint *breakpoint) { if (breakpoint->type == BKPT_SOFT) { - if (target_write_memory(target, breakpoint->address, 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; @@ -864,8 +1018,6 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi { struct watchpoint *wp = target->watchpoints; - if (riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, target->rtos->current_thread - 1); LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target)); /*TODO instead of disassembling the instruction that we think caused the @@ -968,7 +1120,7 @@ static int riscv_examine(struct target *target) /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ - riscv_info_t *info = (riscv_info_t *) target->arch_info; + RISCV_INFO(info); uint32_t dtmcontrol = dtmcontrol_scan(target, 0); LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION); @@ -1000,44 +1152,42 @@ static int old_or_new_riscv_poll(struct target *target) 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); @@ -1110,15 +1260,6 @@ int riscv_halt(struct target *target) return ERROR_FAIL; } - if (riscv_rtos_enabled(target)) { - if (r->rtos_hartid != -1) { - LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid); - target->rtos->current_threadid = r->rtos_hartid + 1; - target->rtos->current_thread = r->rtos_hartid + 1; - } else - LOG_DEBUG("halt requested, but no known RTOS hartid"); - } - return result; } @@ -1140,22 +1281,19 @@ static int riscv_deassert_reset(struct target *target) 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; @@ -1171,13 +1309,12 @@ static int disable_triggers(struct target *target, riscv_reg_t *state) if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; - int hartid = riscv_current_hartid(target); if (r->manual_hwbp_set) { /* Look at every trigger that may have been set. */ riscv_reg_t tselect; if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) return ERROR_FAIL; - for (unsigned t = 0; t < r->trigger_count[hartid]; t++) { + for (unsigned int t = 0; t < r->trigger_count; t++) { if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) return ERROR_FAIL; riscv_reg_t tdata1; @@ -1215,14 +1352,12 @@ static int enable_triggers(struct target *target, riscv_reg_t *state) { RISCV_INFO(r); - int hartid = riscv_current_hartid(target); - if (r->manual_hwbp_set) { /* Look at every trigger that may have been set. */ riscv_reg_t tselect; if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) return ERROR_FAIL; - for (unsigned t = 0; t < r->trigger_count[hartid]; t++) { + for (unsigned int t = 0; t < r->trigger_count; t++) { if (state[t] != 0) { if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) return ERROR_FAIL; @@ -1376,17 +1511,6 @@ static int riscv_target_resume(struct target *target, int current, target_addr_t debug_execution, false); } -static int riscv_select_current_hart(struct target *target) -{ - RISCV_INFO(r); - if (riscv_rtos_enabled(target)) { - if (r->rtos_hartid == -1) - r->rtos_hartid = target->rtos->current_threadid - 1; - return riscv_set_current_hartid(target, r->rtos_hartid); - } else - return riscv_set_current_hartid(target, target->coreid); -} - static int riscv_mmu(struct target *target, int *enabled) { if (!riscv_enable_virt2phys) { @@ -1394,9 +1518,6 @@ static int riscv_mmu(struct target *target, int *enabled) return ERROR_OK; } - if (riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, target->rtos->current_thread - 1); - /* Don't use MMU in explicit or effective M (machine) mode */ riscv_reg_t priv; if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) { @@ -1443,13 +1564,10 @@ static int riscv_address_translate(struct target *target, 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; @@ -1631,8 +1749,8 @@ static int riscv_get_gdb_reg_list_internal(struct target *target, 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("current_hartid=%d, reg_class=%d, read=%d", + r->current_hartid, reg_class, read); if (!target->reg_cache) { LOG_ERROR("Target not initialized. Return ERROR_FAIL."); @@ -1702,8 +1820,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, struct reg_param *reg_params, target_addr_t entry_point, target_addr_t exit_point, int timeout_ms, void *arch_info) { - riscv_info_t *info = (riscv_info_t *) target->arch_info; - 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."); @@ -1768,7 +1885,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, reg_mstatus->type->get(reg_mstatus); current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size); uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE; - buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus, + buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus, ie_mask, 0)); reg_mstatus->type->set(reg_mstatus, mstatus_bytes); @@ -1814,7 +1931,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } /* The current hart id might have been changed in poll(). */ - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + if (riscv_select_current_hart(target) != ERROR_OK) return ERROR_FAIL; if (reg_pc->type->get(reg_pc) != ERROR_OK) @@ -1828,12 +1945,12 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, /* Restore Interrupts */ LOG_DEBUG("Restoring Interrupts"); - buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus); + buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus); reg_mstatus->type->set(reg_mstatus, mstatus_bytes); /* Restore registers */ uint8_t buf[8] = { 0 }; - buf_set_u64(buf, 0, info->xlen[0], saved_pc); + buf_set_u64(buf, 0, info->xlen, saved_pc); if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) return ERROR_FAIL; @@ -1849,7 +1966,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } LOG_DEBUG("restore %s", reg_params[i].reg_name); struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false); - buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]); + 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; @@ -1870,10 +1987,10 @@ static int riscv_checksum_memory(struct target *target, 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; @@ -2004,58 +2121,66 @@ int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason) return ERROR_OK; } -/*** OpenOCD Interface ***/ -int riscv_openocd_poll(struct target *target) +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; list = list->next, i++) { - total_targets++; struct target *t = list->target; riscv_info_t *r = riscv_info(t); enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid); @@ -2113,15 +2238,29 @@ int riscv_openocd_poll(struct target *target) LOG_DEBUG("resume all"); riscv_resume(target, true, 0, 0, 0, false); } + + /* Sample memory if any target is running. */ + for (struct target_list *list = target->head; list; + list = list->next, i++) { + struct target *t = list->target; + if (t->state == TARGET_RUNNING) { + sample_memory(target); + break; + } + } + 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); @@ -2218,32 +2357,86 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec) return ERROR_OK; } -COMMAND_HANDLER(riscv_test_compliance) { - +COMMAND_HANDLER(riscv_set_prefer_sba) +{ struct target *target = get_current_target(CMD_CTX); - RISCV_INFO(r); - - if (CMD_ARGC > 0) { - LOG_ERROR("Command does not take any parameters."); + bool prefer_sba; + LOG_WARNING("`riscv set_prefer_sba` is deprecated. Please use `riscv set_mem_access` instead."); + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); return ERROR_COMMAND_SYNTAX_ERROR; } - - if (r->test_compliance) { - return r->test_compliance(target); + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], prefer_sba); + if (prefer_sba) { + /* Use system bus with highest priority */ + r->mem_access_methods[0] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; } else { - LOG_ERROR("This target does not support this command (may implement an older version of the spec)."); - return ERROR_FAIL; + /* Use progbuf with highest priority */ + r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; } + + /* Reset warning flags */ + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + + return ERROR_OK; } -COMMAND_HANDLER(riscv_set_prefer_sba) +COMMAND_HANDLER(riscv_set_mem_access) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + int progbuf_cnt = 0; + int sysbus_cnt = 0; + int abstract_cnt = 0; + + if (CMD_ARGC < 1 || CMD_ARGC > RISCV_NUM_MEM_ACCESS_METHODS) { + LOG_ERROR("Command takes 1 to %d parameters", RISCV_NUM_MEM_ACCESS_METHODS); return ERROR_COMMAND_SYNTAX_ERROR; } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_prefer_sba); + + /* Check argument validity */ + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if (strcmp("progbuf", CMD_ARGV[i]) == 0) { + progbuf_cnt++; + } else if (strcmp("sysbus", CMD_ARGV[i]) == 0) { + sysbus_cnt++; + } else if (strcmp("abstract", CMD_ARGV[i]) == 0) { + abstract_cnt++; + } else { + LOG_ERROR("Unknown argument '%s'. " + "Must be one of: 'progbuf', 'sysbus' or 'abstract'.", CMD_ARGV[i]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + if (progbuf_cnt > 1 || sysbus_cnt > 1 || abstract_cnt > 1) { + LOG_ERROR("Syntax error - duplicate arguments to `riscv set_mem_access`."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* Args are valid, store them */ + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_UNSPECIFIED; + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if (strcmp("progbuf", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_PROGBUF; + else if (strcmp("sysbus", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_SYSBUS; + else if (strcmp("abstract", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_ABSTRACT; + } + + /* Reset warning flags */ + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + return ERROR_OK; } @@ -2257,109 +2450,180 @@ COMMAND_HANDLER(riscv_set_enable_virtual) 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; } @@ -2377,7 +2641,7 @@ COMMAND_HANDLER(riscv_authdata_read) if (r->authdata_read) { uint32_t value; - if (r->authdata_read(target, &value) != ERROR_OK) + if (r->authdata_read(target, &value, index) != ERROR_OK) return ERROR_FAIL; command_print_sameline(CMD, "0x%08" PRIx32, value); return ERROR_OK; @@ -2389,19 +2653,26 @@ COMMAND_HANDLER(riscv_authdata_read) COMMAND_HANDLER(riscv_authdata_write) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 argument"); + uint32_t value; + unsigned int index = 0; + + if (CMD_ARGC == 0) { + /* nop */ + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value); + } else if (CMD_ARGC == 2) { + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + } else { + LOG_ERROR("Command takes at most 2 arguments"); return ERROR_COMMAND_SYNTAX_ERROR; } struct target *target = get_current_target(CMD_CTX); RISCV_INFO(r); - uint32_t value; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value); - if (r->authdata_write) { - return r->authdata_write(target, value); + return r->authdata_write(target, value, index); } else { LOG_ERROR("authdata_write is not implemented for this target."); return ERROR_FAIL; @@ -2617,13 +2888,40 @@ COMMAND_HANDLER(riscv_set_ebreaku) return ERROR_OK; } +COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key, + unsigned int value) +{ + char full_key[80]; + snprintf(full_key, sizeof(full_key), "%s.%s", section, key); + command_print(CMD, "%-21s %3d", full_key, value); + return 0; +} + +COMMAND_HANDLER(handle_info) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + /* This output format can be fed directly into TCL's "array set". */ + + riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target)); + riscv_enumerate_triggers(target); + riscv_print_info_line(CMD, "hart", "trigger_count", + r->trigger_count); + + if (r->print_info) + return CALL_COMMAND_HANDLER(r->print_info, target); + + return 0; +} + static const struct command_registration riscv_exec_command_handlers[] = { { - .name = "test_compliance", - .handler = riscv_test_compliance, - .usage = "", + .name = "info", + .handler = handle_info, .mode = COMMAND_EXEC, - .help = "Runs a basic compliance test suite against the RISC-V Debug Spec." + .usage = "", + .help = "Displays some information OpenOCD detected about the target." }, { .name = "set_command_timeout_sec", @@ -2647,6 +2945,14 @@ static const struct command_registration riscv_exec_command_handlers[] = { .help = "When on, prefer to use System Bus Access to access memory. " "When off (default), prefer to use the Program Buffer to access memory." }, + { + .name = "set_mem_access", + .handler = riscv_set_mem_access, + .mode = COMMAND_ANY, + .usage = "method1 [method2] [method3]", + .help = "Set which memory access methods shall be used and in which order " + "of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'." + }, { .name = "set_enable_virtual", .handler = riscv_set_enable_virtual, @@ -2659,8 +2965,8 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "expose_csrs", .handler = riscv_set_expose_csrs, - .mode = COMMAND_ANY, - .usage = "n0[-m0][,n1[-m1]]...", + .mode = COMMAND_CONFIG, + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", .help = "Configure a list of inclusive ranges for CSRs to expose in " "addition to the standard ones. This must be executed before " "`init`." @@ -2668,8 +2974,8 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "expose_custom", .handler = riscv_set_expose_custom, - .mode = COMMAND_ANY, - .usage = "n0[-m0][,n1[-m1]]...", + .mode = COMMAND_CONFIG, + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", .help = "Configure a list of inclusive ranges for custom registers to " "expose. custom0 is accessed as abstract register number 0xc000, " "etc. This must be executed before `init`." @@ -2677,16 +2983,18 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "authdata_read", .handler = riscv_authdata_read, - .usage = "", + .usage = "[index]", .mode = COMMAND_ANY, - .help = "Return the 32-bit value read from authdata." + .help = "Return the 32-bit value read from authdata or authdata0 " + "(index=0), or authdata1 (index=1)." }, { .name = "authdata_write", .handler = riscv_authdata_write, .mode = COMMAND_ANY, - .usage = "value", - .help = "Write the 32-bit value to authdata." + .usage = "[index] value", + .help = "Write the 32-bit value to authdata or authdata0 (index=0), " + "or authdata1 (index=1)." }, { .name = "dmi_read", @@ -2836,6 +3144,7 @@ static unsigned int riscv_data_bits(struct target *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, @@ -2889,49 +3198,37 @@ void riscv_info_init(struct target *target, riscv_info_t *r) r->dtm_version = 1; r->registers_initialized = false; r->current_hartid = target->coreid; + r->version_specific = NULL; memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id)); - for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) - r->xlen[h] = -1; + 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); @@ -2941,17 +3238,9 @@ static int riscv_resume_go_all_harts(struct target *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!"); @@ -2970,7 +3259,7 @@ int riscv_step_rtos_hart(struct target *target) return ERROR_OK; } -bool riscv_supports_extension(struct target *target, int hartid, char letter) +bool riscv_supports_extension(struct target *target, char letter) { RISCV_INFO(r); unsigned num; @@ -2980,24 +3269,13 @@ bool riscv_supports_extension(struct target *target, int hartid, char letter) num = letter - 'A'; else return false; - return r->misa[hartid] & (1 << num); + return r->misa & BIT(num); } 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) @@ -3008,16 +3286,10 @@ int riscv_set_current_hartid(struct target *target, int hartid) int previous_hartid = riscv_current_hartid(target); r->current_hartid = hartid; - assert(riscv_hart_enabled(target, hartid)); LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid); if (r->select_current_hart(target) != ERROR_OK) return ERROR_FAIL; - /* This might get called during init, in which case we shouldn't be - * setting up the register cache. */ - if (target_was_examined(target) && riscv_rtos_enabled(target)) - riscv_invalidate_register_cache(target); - return ERROR_OK; } @@ -3041,19 +3313,6 @@ 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) @@ -3064,11 +3323,6 @@ int riscv_count_harts(struct target *target) 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 @@ -3085,7 +3339,7 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write) (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)) return true; - /* Most CSRs won't change value on us, but we can't assume it about rbitrary + /* Most CSRs won't change value on us, but we can't assume it about arbitrary * CSRs. */ switch (regno) { case GDB_REGNO_DPC: @@ -3122,75 +3376,67 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write) * This function is called when the debug user wants to change the value of a * register. The new value may be cached, and may not be written until the hart * is resumed. */ -int riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v) -{ - return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v); -} - -int riscv_set_register_on_hart(struct target *target, int hartid, - enum gdb_regno regid, uint64_t value) +int riscv_set_register(struct target *target, enum gdb_regno regid, riscv_reg_t value) { RISCV_INFO(r); - LOG_DEBUG("{%d} %s <- %" PRIx64, hartid, gdb_regno_name(regid), value); + LOG_DEBUG("[%s] %s <- %" PRIx64, target_name(target), gdb_regno_name(regid), value); assert(r->set_register); + keep_alive(); + /* TODO: Hack to deal with gdb that thinks these registers still exist. */ if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 && - riscv_supports_extension(target, hartid, 'E')) + riscv_supports_extension(target, 'E')) return ERROR_OK; struct reg *reg = &target->reg_cache->reg_list[regid]; buf_set_u64(reg->value, 0, reg->size, value); - int result = r->set_register(target, hartid, regid, value); + int result = r->set_register(target, regid, value); if (result == ERROR_OK) reg->valid = gdb_regno_cacheable(regid, true); else reg->valid = false; - LOG_DEBUG("[%s]{%d} wrote 0x%" PRIx64 " to %s valid=%d", - target_name(target), hartid, value, reg->name, reg->valid); + LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d", + target_name(target), value, reg->name, reg->valid); return result; } int riscv_get_register(struct target *target, riscv_reg_t *value, - enum gdb_regno r) -{ - return riscv_get_register_on_hart(target, value, - riscv_current_hartid(target), r); -} - -int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value, - int hartid, enum gdb_regno regid) + enum gdb_regno regid) { RISCV_INFO(r); + keep_alive(); + struct reg *reg = &target->reg_cache->reg_list[regid]; if (!reg->exist) { - LOG_DEBUG("[%s]{%d} %s does not exist.", - target_name(target), hartid, gdb_regno_name(regid)); + LOG_DEBUG("[%s] %s does not exist.", + target_name(target), gdb_regno_name(regid)); return ERROR_FAIL; } - if (reg && reg->valid && hartid == riscv_current_hartid(target)) { + if (reg && reg->valid) { *value = buf_get_u64(reg->value, 0, reg->size); - LOG_DEBUG("{%d} %s: %" PRIx64 " (cached)", hartid, + LOG_DEBUG("[%s] %s: %" PRIx64 " (cached)", target_name(target), gdb_regno_name(regid), *value); return ERROR_OK; } /* TODO: Hack to deal with gdb that thinks these registers still exist. */ if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && - riscv_supports_extension(target, hartid, 'E')) { + riscv_supports_extension(target, 'E')) { *value = 0; return ERROR_OK; } - int result = r->get_register(target, value, hartid, regid); + int result = r->get_register(target, value, regid); if (result == ERROR_OK) reg->valid = gdb_regno_cacheable(regid, false); - LOG_DEBUG("{%d} %s: %" PRIx64, hartid, gdb_regno_name(regid), *value); + LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target), + gdb_regno_name(regid), *value); return result; } @@ -3216,7 +3462,7 @@ enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid) size_t riscv_debug_buffer_size(struct target *target) { RISCV_INFO(r); - return r->debug_buffer_size[riscv_current_hartid(target)]; + return r->debug_buffer_size; } int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn) @@ -3262,15 +3508,6 @@ int riscv_dmi_write_u64_bits(struct target *target) return r->dmi_write_u64_bits(target); } -bool riscv_hart_enabled(struct target *target, int hartid) -{ - /* FIXME: Add a hart mask to the RTOS. */ - if (riscv_rtos_enabled(target)) - return hartid < riscv_count_harts(target); - - return hartid == target->coreid; -} - /** * Count triggers, and initialize trigger_count for each hart. * trigger_count is initialized even if this function fails to discover @@ -3287,58 +3524,57 @@ int riscv_enumerate_triggers(struct target *target) r->triggers_enumerated = true; /* At the very least we tried. */ - for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; + riscv_reg_t tselect; + int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT); + /* If tselect is not readable, the trigger module is likely not + * implemented. There are no triggers to enumerate then and no error + * should be thrown. */ + if (result != ERROR_OK) { + LOG_DEBUG("[%s] Cannot access tselect register. " + "Assuming that triggers are not implemented.", target_name(target)); + r->trigger_count = 0; + return ERROR_OK; + } - riscv_reg_t tselect; - int result = riscv_get_register_on_hart(target, &tselect, hartid, - GDB_REGNO_TSELECT); + for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) { + r->trigger_count = t; + + /* If we can't write tselect, then this hart does not support triggers. */ + if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) + break; + uint64_t tselect_rb; + result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT); + if (result != ERROR_OK) + return result; + /* Mask off the top bit, which is used as tdrmode in old + * implementations. */ + tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1)); + if (tselect_rb != t) + break; + uint64_t tdata1; + result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1); if (result != ERROR_OK) return result; - for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) { - r->trigger_count[hartid] = t; - - /* 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); - if (result != ERROR_OK) - return result; - /* Mask off the top bit, which is used as tdrmode in old - * implementations. */ - tselect_rb &= ~(1ULL << (riscv_xlen(target)-1)); - if (tselect_rb != t) + int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); + if (type == 0) + break; + switch (type) { + case 1: + /* On these older cores we don't support software using + * triggers. */ + riscv_set_register(target, GDB_REGNO_TDATA1, 0); break; - 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 2: + 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; } @@ -3549,8 +3785,8 @@ static int register_get(struct reg *reg) } reg->valid = gdb_regno_cacheable(reg->number, false); char *str = buf_to_hex_str(reg->value, reg->size); - LOG_DEBUG("[%d]{%d} read 0x%s from %s (valid=%d)", target->coreid, - riscv_current_hartid(target), str, reg->name, reg->valid); + LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target), + str, reg->name, reg->valid); free(str); return ERROR_OK; } @@ -3562,10 +3798,15 @@ static int register_set(struct reg *reg, uint8_t *buf) RISCV_INFO(r); char *str = buf_to_hex_str(buf, reg->size); - LOG_DEBUG("[%d]{%d} write 0x%s to %s (valid=%d)", target->coreid, - riscv_current_hartid(target), str, reg->name, reg->valid); + LOG_DEBUG("[%s] write 0x%s to %s (valid=%d)", target_name(target), + str, reg->name, reg->valid); free(str); + /* Exit early for writing x0, which on the hardware would be ignored, and we + * don't want to update our cache. */ + if (reg->number == GDB_REGNO_ZERO) + return ERROR_OK; + memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8)); reg->valid = gdb_regno_cacheable(reg->number, true); @@ -3625,13 +3866,10 @@ int riscv_init_registers(struct target *target) target->reg_cache->name = "RISC-V Registers"; target->reg_cache->num_regs = GDB_REGNO_COUNT; - if (expose_custom) { - for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) { - for (unsigned number = expose_custom[i].low; - number <= expose_custom[i].high; - number++) - target->reg_cache->num_regs++; - } + if (!list_empty(&info->expose_custom)) { + range_list_t *entry; + list_for_each_entry(entry, &info->expose_custom, list) + target->reg_cache->num_regs += entry->high - entry->low + 1; } LOG_DEBUG("create register cache for %d registers", @@ -3650,8 +3888,6 @@ int riscv_init_registers(struct target *target) return ERROR_FAIL; char *reg_name = info->reg_names; - int hartid = riscv_current_hartid(target); - static struct reg_feature feature_cpu = { .name = "org.gnu.gdb.riscv.cpu" }; @@ -3709,35 +3945,35 @@ int riscv_init_registers(struct target *target) */ info->vector_uint8.type = &type_uint8; - info->vector_uint8.count = info->vlenb[hartid]; + info->vector_uint8.count = info->vlenb; info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint8_vector.id = "bytes"; info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint8_vector.reg_type_vector = &info->vector_uint8; info->vector_uint16.type = &type_uint16; - info->vector_uint16.count = info->vlenb[hartid] / 2; + info->vector_uint16.count = info->vlenb / 2; info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint16_vector.id = "shorts"; info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint16_vector.reg_type_vector = &info->vector_uint16; info->vector_uint32.type = &type_uint32; - info->vector_uint32.count = info->vlenb[hartid] / 4; + info->vector_uint32.count = info->vlenb / 4; info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint32_vector.id = "words"; info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint32_vector.reg_type_vector = &info->vector_uint32; info->vector_uint64.type = &type_uint64; - info->vector_uint64.count = info->vlenb[hartid] / 8; + info->vector_uint64.count = info->vlenb / 8; info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint64_vector.id = "longs"; info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint64_vector.reg_type_vector = &info->vector_uint64; info->vector_uint128.type = &type_uint128; - info->vector_uint128.count = info->vlenb[hartid] / 16; + info->vector_uint128.count = info->vlenb / 16; info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint128_vector.id = "quads"; info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR; @@ -3745,28 +3981,28 @@ int riscv_init_registers(struct target *target) info->vector_fields[0].name = "b"; info->vector_fields[0].type = &info->type_uint8_vector; - if (info->vlenb[hartid] >= 2) { + if (info->vlenb >= 2) { info->vector_fields[0].next = info->vector_fields + 1; info->vector_fields[1].name = "s"; info->vector_fields[1].type = &info->type_uint16_vector; } else { info->vector_fields[0].next = NULL; } - if (info->vlenb[hartid] >= 4) { + if (info->vlenb >= 4) { info->vector_fields[1].next = info->vector_fields + 2; info->vector_fields[2].name = "w"; info->vector_fields[2].type = &info->type_uint32_vector; } else { info->vector_fields[1].next = NULL; } - if (info->vlenb[hartid] >= 8) { + if (info->vlenb >= 8) { info->vector_fields[2].next = info->vector_fields + 3; info->vector_fields[3].name = "l"; info->vector_fields[3].type = &info->type_uint64_vector; } else { info->vector_fields[2].next = NULL; } - if (info->vlenb[hartid] >= 16) { + if (info->vlenb >= 16) { info->vector_fields[3].next = info->vector_fields + 4; info->vector_fields[4].name = "q"; info->vector_fields[4].type = &info->type_uint128_vector; @@ -3791,7 +4027,6 @@ int riscv_init_registers(struct target *target) 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)); @@ -3818,7 +4053,7 @@ int riscv_init_registers(struct target *target) * of other things to break in that case as well. */ if (number <= GDB_REGNO_XPR31) { r->exist = number <= GDB_REGNO_XPR15 || - !riscv_supports_extension(target, hartid, 'E'); + !riscv_supports_extension(target, 'E'); /* TODO: For now we fake that all GPRs exist because otherwise gdb * doesn't work. */ r->exist = true; @@ -3930,13 +4165,13 @@ int riscv_init_registers(struct target *target) r->feature = &feature_cpu; } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { r->caller_save = true; - if (riscv_supports_extension(target, 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 { @@ -4067,7 +4302,7 @@ int riscv_init_registers(struct target *target) case CSR_FFLAGS: case CSR_FRM: case CSR_FCSR: - r->exist = riscv_supports_extension(target, hartid, 'F'); + r->exist = riscv_supports_extension(target, 'F'); r->group = "float"; r->feature = &feature_fpu; break; @@ -4081,15 +4316,15 @@ int riscv_init_registers(struct target *target) case CSR_SCAUSE: case CSR_STVAL: case CSR_SATP: - r->exist = riscv_supports_extension(target, hartid, 'S'); + r->exist = riscv_supports_extension(target, 'S'); break; case CSR_MEDELEG: case CSR_MIDELEG: /* "In systems with only M-mode, or with both M-mode and * U-mode but without U-mode trap support, the medeleg and * mideleg registers should not exist." */ - r->exist = riscv_supports_extension(target, hartid, 'S') || - riscv_supports_extension(target, hartid, 'N'); + r->exist = riscv_supports_extension(target, 'S') || + riscv_supports_extension(target, 'N'); break; case CSR_PMPCFG1: @@ -4166,18 +4401,25 @@ int riscv_init_registers(struct target *target) case CSR_VL: case CSR_VTYPE: case CSR_VLENB: - r->exist = riscv_supports_extension(target, hartid, 'V'); + r->exist = riscv_supports_extension(target, 'V'); break; } - if (!r->exist && expose_csr) { - for (unsigned i = 0; expose_csr[i].low <= expose_csr[i].high; i++) { - if (csr_number >= expose_csr[i].low && csr_number <= expose_csr[i].high) { - LOG_INFO("Exposing additional CSR %d", csr_number); + if (!r->exist && !list_empty(&info->expose_csr)) { + range_list_t *entry; + list_for_each_entry(entry, &info->expose_csr, list) + if ((entry->low <= csr_number) && (csr_number <= entry->high)) { + if (entry->name) { + *reg_name = 0; + r->name = entry->name; + } + + LOG_DEBUG("Exposing additional CSR %d (name=%s)", + csr_number, entry->name ? entry->name : reg_name); + r->exist = true; break; } - } } } else if (number == GDB_REGNO_PRIV) { @@ -4188,8 +4430,8 @@ int riscv_init_registers(struct target *target) } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) { r->caller_save = false; - r->exist = riscv_supports_extension(target, hartid, 'V') && info->vlenb[hartid]; - r->size = info->vlenb[hartid] * 8; + r->exist = riscv_supports_extension(target, 'V') && info->vlenb; + r->size = info->vlenb * 8; sprintf(reg_name, "v%d", number - GDB_REGNO_V0); r->group = "vector"; r->feature = &feature_vector; @@ -4197,10 +4439,10 @@ int riscv_init_registers(struct target *target) } else if (number >= GDB_REGNO_COUNT) { /* Custom registers. */ - assert(expose_custom); + assert(!list_empty(&info->expose_custom)); + + range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list); - range_t *range = &expose_custom[custom_range_index]; - assert(range->low <= range->high); unsigned custom_number = range->low + custom_within_range; r->group = "custom"; @@ -4212,18 +4454,27 @@ int riscv_init_registers(struct target *target) ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number; sprintf(reg_name, "custom%d", custom_number); + if (range->name) { + *reg_name = 0; + r->name = range->name; + } + + LOG_DEBUG("Exposing additional custom register %d (name=%s)", + number, range->name ? range->name : reg_name); + custom_within_range++; if (custom_within_range > range->high - range->low) { custom_within_range = 0; - custom_range_index++; + list_rotate_left(&info->expose_custom); } } - if (reg_name[0]) + if (reg_name[0]) { r->name = reg_name; - reg_name += strlen(reg_name) + 1; - assert(reg_name < info->reg_names + target->reg_cache->num_regs * - max_reg_name_len); + 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]; }