diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am index 4b6a74f0b..189edb3e6 100644 --- a/src/target/riscv/Makefile.am +++ b/src/target/riscv/Makefile.am @@ -2,17 +2,31 @@ noinst_LTLIBRARIES += %D%/libriscv.la %C%_libriscv_la_SOURCES = \ - %D%/asm.h \ %D%/batch.h \ %D%/debug_defines.h \ + %D%/debug_reg_printer.h \ %D%/encoding.h \ + %D%/field_helpers.h \ %D%/gdb_regs.h \ %D%/opcodes.h \ %D%/program.h \ %D%/riscv.h \ + %D%/riscv_reg_impl.h \ + %D%/riscv_reg.h \ + %D%/riscv-011.h \ + %D%/riscv-011_reg.h \ + %D%/riscv-013.h \ + %D%/riscv-013_reg.h \ %D%/batch.c \ %D%/program.c \ %D%/riscv-011.c \ + %D%/riscv-011_reg.c \ %D%/riscv-013.c \ + %D%/riscv-013_reg.c \ %D%/riscv.c \ - %D%/riscv_semihosting.c + %D%/riscv_reg.c \ + %D%/riscv_semihosting.c \ + %D%/debug_defines.c \ + %D%/debug_reg_printer.c + +STARTUP_TCL_SRCS += %D%/startup.tcl diff --git a/src/target/riscv/asm.h b/src/target/riscv/asm.h deleted file mode 100644 index 6ceb8c9bd..000000000 --- a/src/target/riscv/asm.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#ifndef TARGET__RISCV__ASM_H -#define TARGET__RISCV__ASM_H - -#include "riscv.h" - -/*** Version-independent functions that we don't want in the main address space. ***/ - -static uint32_t load(const struct target *target, unsigned int rd, - unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t load(const struct target *target, unsigned int rd, - unsigned int base, uint16_t offset) -{ - switch (riscv_xlen(target)) { - case 32: - return lw(rd, base, offset); - case 64: - return ld(rd, base, offset); - } - assert(0); - return 0; /* Silence -Werror=return-type */ -} - -static uint32_t store(const struct target *target, unsigned int src, - unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t store(const struct target *target, unsigned int src, - unsigned int base, uint16_t offset) -{ - switch (riscv_xlen(target)) { - case 32: - return sw(src, base, offset); - case 64: - return sd(src, base, offset); - } - assert(0); - return 0; /* Silence -Werror=return-type */ -} - -#endif diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c index e3f8ff3d4..ec68b3798 100644 --- a/src/target/riscv/batch.c +++ b/src/target/riscv/batch.c @@ -6,67 +6,90 @@ #include "batch.h" #include "debug_defines.h" +#include "debug_reg_printer.h" #include "riscv.h" +#include "field_helpers.h" -#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) -#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) - +// TODO: DTM_DMI_MAX_ADDRESS_LENGTH should be reduced to 32 (per the debug spec) #define DTM_DMI_MAX_ADDRESS_LENGTH ((1< 0); + assert(abits <= DTM_DMI_MAX_ADDRESS_LENGTH); + + return abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH; +} + +struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans) +{ + scans += BATCH_RESERVED_SCANS; struct riscv_batch *out = calloc(1, sizeof(*out)); - if (!out) - goto error0; + if (!out) { + LOG_ERROR("Failed to allocate struct riscv_batch"); + return NULL; + } + out->target = target; out->allocated_scans = scans; - out->idle_count = idle; - out->data_out = malloc(sizeof(*out->data_out) * (scans) * DMI_SCAN_BUF_SIZE); + out->last_scan = RISCV_SCAN_TYPE_INVALID; + out->was_run = false; + out->last_scan_delay = 0; + + out->data_out = NULL; + out->data_in = NULL; + out->fields = NULL; + out->delay_classes = NULL; + out->bscan_ctxt = NULL; + out->read_keys = NULL; + + /* FIXME: There is potential for memory usage reduction. We could allocate + * smaller buffers than DMI_SCAN_BUF_SIZE (that is, buffers that correspond to + * the real DR scan length on the given target) */ + out->data_out = malloc(sizeof(*out->data_out) * scans * DMI_SCAN_BUF_SIZE); if (!out->data_out) { LOG_ERROR("Failed to allocate data_out in RISC-V batch."); - goto error1; + goto alloc_error; }; - out->data_in = malloc(sizeof(*out->data_in) * (scans) * DMI_SCAN_BUF_SIZE); + out->data_in = malloc(sizeof(*out->data_in) * scans * DMI_SCAN_BUF_SIZE); if (!out->data_in) { LOG_ERROR("Failed to allocate data_in in RISC-V batch."); - goto error2; + goto alloc_error; } - out->fields = malloc(sizeof(*out->fields) * (scans)); + out->fields = malloc(sizeof(*out->fields) * scans); if (!out->fields) { LOG_ERROR("Failed to allocate fields in RISC-V batch."); - goto error3; + goto alloc_error; + } + out->delay_classes = malloc(sizeof(*out->delay_classes) * scans); + if (!out->delay_classes) { + LOG_ERROR("Failed to allocate delay_classes in RISC-V batch."); + goto alloc_error; } if (bscan_tunnel_ir_width != 0) { - out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * (scans)); + out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * scans); if (!out->bscan_ctxt) { LOG_ERROR("Failed to allocate bscan_ctxt in RISC-V batch."); - goto error4; + goto alloc_error; } } - out->last_scan = RISCV_SCAN_TYPE_INVALID; - out->read_keys = malloc(sizeof(*out->read_keys) * (scans)); + out->read_keys = malloc(sizeof(*out->read_keys) * scans); if (!out->read_keys) { LOG_ERROR("Failed to allocate read_keys in RISC-V batch."); - goto error5; + goto alloc_error; } + return out; -error5: - free(out->bscan_ctxt); -error4: - free(out->fields); -error3: - free(out->data_in); -error2: - free(out->data_out); -error1: - free(out); -error0: +alloc_error: + riscv_batch_free(out); return NULL; } @@ -75,6 +98,7 @@ void riscv_batch_free(struct riscv_batch *batch) free(batch->data_in); free(batch->data_out); free(batch->fields); + free(batch->delay_classes); free(batch->bscan_ctxt); free(batch->read_keys); free(batch); @@ -82,32 +106,209 @@ void riscv_batch_free(struct riscv_batch *batch) bool riscv_batch_full(struct riscv_batch *batch) { - return batch->used_scans > (batch->allocated_scans - 4); + return riscv_batch_available_scans(batch) == 0; } -int riscv_batch_run(struct riscv_batch *batch) +static bool riscv_batch_was_scan_busy(const struct riscv_batch *batch, + size_t scan_idx) { - if (batch->used_scans == 0) { - LOG_DEBUG("Ignoring empty batch."); - return ERROR_OK; + assert(batch->was_run); + assert(scan_idx < batch->used_scans); + const struct scan_field *field = batch->fields + scan_idx; + assert(field->in_value); + const uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits); + return get_field(in, DTM_DMI_OP) == DTM_DMI_OP_BUSY; +} + +static void add_idle_before_batch(const struct riscv_batch *batch, size_t start_idx, + const struct riscv_scan_delays *delays) +{ + if (!batch->was_run) + return; + /* Get the delay type of the scan that resulted in the busy response. + * Since DMI interactions always end with a NOP, if "start_idx" is zero + * the base delay value is used. + */ + const enum riscv_scan_delay_class delay_class = start_idx > 0 + ? batch->delay_classes[start_idx - 1] + : RISCV_DELAY_BASE; + const unsigned int new_delay = riscv_scan_get_delay(delays, delay_class); + if (new_delay <= batch->last_scan_delay) + return; + const unsigned int idle_change = new_delay - batch->last_scan_delay; + LOG_TARGET_DEBUG(batch->target, "Adding %u idle cycles before the batch.", + idle_change); + jtag_add_runtest(idle_change, TAP_IDLE); +} + +static unsigned int get_delay(const struct riscv_batch *batch, size_t scan_idx, + const struct riscv_scan_delays *delays, bool resets_delays, + size_t reset_delays_after) +{ + assert(batch); + assert(scan_idx < batch->used_scans); + const bool delays_were_reset = resets_delays + && (scan_idx >= reset_delays_after); + const enum riscv_scan_delay_class delay_class = + batch->delay_classes[scan_idx]; + const unsigned int delay = riscv_scan_get_delay(delays, delay_class); + return delays_were_reset ? 0 : delay; +} + +static unsigned int decode_dmi(const struct riscv_batch *batch, char *text, + uint32_t address, uint32_t data) +{ + static const struct { + uint32_t address; + enum riscv_debug_reg_ordinal ordinal; + } description[] = { + {DM_DMCONTROL, DM_DMCONTROL_ORDINAL}, + {DM_DMSTATUS, DM_DMSTATUS_ORDINAL}, + {DM_ABSTRACTCS, DM_ABSTRACTCS_ORDINAL}, + {DM_COMMAND, DM_COMMAND_ORDINAL}, + {DM_SBCS, DM_SBCS_ORDINAL} + }; + + for (unsigned int i = 0; i < ARRAY_SIZE(description); i++) { + if (riscv_get_dmi_address(batch->target, description[i].address) + == address) { + const riscv_debug_reg_ctx_t context = { + .XLEN = { .value = 0, .is_set = false }, + .DXLEN = { .value = 0, .is_set = false }, + .abits = { .value = 0, .is_set = false }, + }; + return riscv_debug_reg_to_s(text, description[i].ordinal, + context, data, RISCV_DEBUG_REG_HIDE_ALL_0); + } + } + if (text) + text[0] = '\0'; + return 0; +} + +static void log_dmi_decoded(const struct riscv_batch *batch, bool write, + uint32_t address, uint32_t data) +{ + const size_t size = decode_dmi(batch, /* text */ NULL, address, data) + 1; + char * const decoded = malloc(size); + if (!decoded) { + LOG_ERROR("Not enough memory to allocate %zu bytes.", size); + return; + } + decode_dmi(batch, decoded, address, data); + LOG_DEBUG("%s: %s", write ? "write" : "read", decoded); + free(decoded); +} + +static void log_batch(const struct riscv_batch *batch, size_t start_idx, + const struct riscv_scan_delays *delays, bool resets_delays, + size_t reset_delays_after) +{ + if (debug_level < LOG_LVL_DEBUG) + return; + + const unsigned int abits = riscv_get_dmi_address_bits(batch->target); + + /* Determine the "op" and "address" of the scan that preceded the first + * executed scan. + * FIXME: The code here assumes that there were no DMI operations between + * the last execution of the batch and the current one. + * Caching the info about the last executed DMI scan in "dm013_info_t" + * would be a more robust solution. + */ + bool last_scan_was_read = false; + uint32_t last_scan_address = (uint32_t)(-1) /* to silence maybe-uninitialized */; + if (start_idx > 0) { + const struct scan_field * const field = &batch->fields[start_idx - 1]; + assert(field->out_value); + last_scan_was_read = buf_get_u32(field->out_value, DTM_DMI_OP_OFFSET, + DTM_DMI_OP_LENGTH) == DTM_DMI_OP_READ; + last_scan_address = buf_get_u32(field->out_value, + DTM_DMI_ADDRESS_OFFSET, abits); } - riscv_batch_add_nop(batch); + /* Decode and log every executed scan */ + for (size_t i = start_idx; i < batch->used_scans; ++i) { + static const char * const op_string[] = {"-", "r", "w", "?"}; + const unsigned int delay = get_delay(batch, i, delays, resets_delays, + reset_delays_after); + const struct scan_field * const field = &batch->fields[i]; - for (size_t i = 0; i < batch->used_scans; ++i) { + assert(field->out_value); + const unsigned int out_op = buf_get_u32(field->out_value, + DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); + const uint32_t out_data = buf_get_u32(field->out_value, + DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); + const uint32_t out_address = buf_get_u32(field->out_value, + DTM_DMI_ADDRESS_OFFSET, abits); + if (field->in_value) { + static const char * const status_string[] = { + "+", "?", "F", "b" + }; + const unsigned int in_op = buf_get_u32(field->in_value, + DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); + const uint32_t in_data = buf_get_u32(field->in_value, + DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); + const uint32_t in_address = buf_get_u32(field->in_value, + DTM_DMI_ADDRESS_OFFSET, abits); + + LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32 + " -> %s %08" PRIx32 " @%02" PRIx32 "; %ui", + field->num_bits, op_string[out_op], out_data, out_address, + status_string[in_op], in_data, in_address, delay); + + if (last_scan_was_read && in_op == DTM_DMI_OP_SUCCESS) + log_dmi_decoded(batch, /*write*/ false, + last_scan_address, in_data); + } else { + LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32 " -> ?; %ui", + field->num_bits, op_string[out_op], out_data, out_address, + delay); + } + + if (out_op == DTM_DMI_OP_WRITE) + log_dmi_decoded(batch, /*write*/ true, out_address, + out_data); + + last_scan_was_read = out_op == DTM_DMI_OP_READ; + last_scan_address = out_address; + } +} + +int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx, + const struct riscv_scan_delays *delays, bool resets_delays, + size_t reset_delays_after) +{ + assert(batch->used_scans); + assert(start_idx < batch->used_scans); + assert(batch->last_scan == RISCV_SCAN_TYPE_NOP); + assert(!batch->was_run || riscv_batch_was_scan_busy(batch, start_idx)); + assert(start_idx == 0 || !riscv_batch_was_scan_busy(batch, start_idx - 1)); + + if (batch->was_run) + add_idle_before_batch(batch, start_idx, delays); + + LOG_TARGET_DEBUG(batch->target, "Running batch of scans [%zu, %zu)", + start_idx, batch->used_scans); + + unsigned int delay = 0 /* to silence maybe-uninitialized */; + for (size_t i = start_idx; i < batch->used_scans; ++i) { if (bscan_tunnel_ir_width != 0) - riscv_add_bscan_tunneled_scan(batch->target, batch->fields+i, batch->bscan_ctxt+i); + riscv_add_bscan_tunneled_scan(batch->target->tap, batch->fields + i, + batch->bscan_ctxt + i); else jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE); - if (batch->idle_count > 0) - jtag_add_runtest(batch->idle_count, TAP_IDLE); + delay = get_delay(batch, i, delays, resets_delays, + reset_delays_after); + if (delay > 0) + jtag_add_runtest(delay, TAP_IDLE); } keep_alive(); if (jtag_execute_queue() != ERROR_OK) { - LOG_ERROR("Unable to execute JTAG queue"); + LOG_TARGET_ERROR(batch->target, "Unable to execute JTAG queue"); return ERROR_FAIL; } @@ -115,40 +316,69 @@ int riscv_batch_run(struct riscv_batch *batch) if (bscan_tunnel_ir_width != 0) { /* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */ - for (size_t i = 0; i < batch->used_scans; ++i) { + for (size_t i = start_idx; i < batch->used_scans; ++i) { if ((batch->fields + i)->in_value) buffer_shr((batch->fields + i)->in_value, DMI_SCAN_BUF_SIZE, 1); } } - for (size_t i = 0; i < batch->used_scans; ++i) - dump_field(batch->idle_count, batch->fields + i); - + log_batch(batch, start_idx, delays, resets_delays, reset_delays_after); + batch->was_run = true; + batch->last_scan_delay = delay; return ERROR_OK; } -void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned int address, uint64_t data) +void riscv_batch_add_dmi_write(struct riscv_batch *batch, uint32_t address, uint32_t data, + bool read_back, enum riscv_scan_delay_class delay_class) { + // TODO: Check that the bit width of "address" is no more than dtmcs.abits, + // otherwise return an error (during batch creation or when the batch is executed). + assert(batch->used_scans < batch->allocated_scans); struct scan_field *field = batch->fields + batch->used_scans; - field->num_bits = riscv_dmi_write_u64_bits(batch->target); - field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE); - field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE); - riscv_fill_dmi_write_u64(batch->target, (char *)field->out_value, address, data); - riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value); + + field->num_bits = get_dmi_scan_length(batch->target); + assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH); + + uint8_t *out_value = batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE; + uint8_t *in_value = batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE; + + field->out_value = out_value; + riscv_fill_dmi_write(batch->target, out_value, address, data); + + if (read_back) { + field->in_value = in_value; + riscv_fill_dm_nop(batch->target, in_value); + } else { + field->in_value = NULL; + } + + batch->delay_classes[batch->used_scans] = delay_class; batch->last_scan = RISCV_SCAN_TYPE_WRITE; batch->used_scans++; } -size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned int address) +size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, uint32_t address, + enum riscv_scan_delay_class delay_class) { + // TODO: Check that the bit width of "address" is no more than dtmcs.abits, + // otherwise return an error (during batch creation or when the batch is executed). + assert(batch->used_scans < batch->allocated_scans); struct scan_field *field = batch->fields + batch->used_scans; - field->num_bits = riscv_dmi_write_u64_bits(batch->target); - field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE); - field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE); - riscv_fill_dmi_read_u64(batch->target, (char *)field->out_value, address); - riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value); + + field->num_bits = get_dmi_scan_length(batch->target); + assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH); + + uint8_t *out_value = batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE; + uint8_t *in_value = batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE; + + field->out_value = out_value; + field->in_value = in_value; + riscv_fill_dmi_read(batch->target, out_value, address); + riscv_fill_dm_nop(batch->target, in_value); + + batch->delay_classes[batch->used_scans] = delay_class; batch->last_scan = RISCV_SCAN_TYPE_READ; batch->used_scans++; @@ -156,21 +386,21 @@ size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned int address) return batch->read_keys_used++; } -uint32_t riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key) +uint32_t riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key) { assert(key < batch->read_keys_used); size_t index = batch->read_keys[key]; - assert(index <= batch->used_scans); + assert(index < batch->used_scans); uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index; /* extract "op" field from the DMI read result */ return buf_get_u32(base, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); } -uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key) +uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key) { assert(key < batch->read_keys_used); size_t index = batch->read_keys[key]; - assert(index <= batch->used_scans); + assert(index < batch->used_scans); uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index; /* extract "data" field from the DMI read result */ return buf_get_u32(base, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); @@ -180,48 +410,48 @@ void riscv_batch_add_nop(struct riscv_batch *batch) { assert(batch->used_scans < batch->allocated_scans); struct scan_field *field = batch->fields + batch->used_scans; - field->num_bits = riscv_dmi_write_u64_bits(batch->target); - field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE); - field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE); - riscv_fill_dmi_nop_u64(batch->target, (char *)field->out_value); - riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value); + + field->num_bits = get_dmi_scan_length(batch->target); + assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH); + + uint8_t *out_value = batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE; + uint8_t *in_value = batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE; + + field->out_value = out_value; + field->in_value = in_value; + riscv_fill_dm_nop(batch->target, out_value); + riscv_fill_dm_nop(batch->target, in_value); + + /* DMI NOP never triggers any debug module operation, + * so the shortest (base) delay can be used. */ + batch->delay_classes[batch->used_scans] = RISCV_DELAY_BASE; batch->last_scan = RISCV_SCAN_TYPE_NOP; batch->used_scans++; } -void dump_field(int idle, const struct scan_field *field) -{ - static const char * const op_string[] = {"-", "r", "w", "?"}; - static const char * const status_string[] = {"+", "?", "F", "b"}; - - if (debug_level < LOG_LVL_DEBUG) - return; - - assert(field->out_value); - uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits); - unsigned int out_op = get_field(out, DTM_DMI_OP); - unsigned int out_data = get_field(out, DTM_DMI_DATA); - unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET; - - if (field->in_value) { - uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits); - unsigned int in_op = get_field(in, DTM_DMI_OP); - unsigned int in_data = get_field(in, DTM_DMI_DATA); - unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET; - - log_printf_lf(LOG_LVL_DEBUG, - __FILE__, __LINE__, __PRETTY_FUNCTION__, - "%ub %s %08x @%02x -> %s %08x @%02x; %di", - field->num_bits, op_string[out_op], out_data, out_address, - status_string[in_op], in_data, in_address, idle); - } else { - log_printf_lf(LOG_LVL_DEBUG, - __FILE__, __LINE__, __PRETTY_FUNCTION__, "%ub %s %08x @%02x -> ?; %di", - field->num_bits, op_string[out_op], out_data, out_address, idle); - } -} - size_t riscv_batch_available_scans(struct riscv_batch *batch) { - return batch->allocated_scans - batch->used_scans - 4; + assert(batch->allocated_scans >= (batch->used_scans + BATCH_RESERVED_SCANS)); + return batch->allocated_scans - batch->used_scans - BATCH_RESERVED_SCANS; +} + +bool riscv_batch_was_batch_busy(const struct riscv_batch *batch) +{ + assert(batch->was_run); + assert(batch->used_scans); + assert(batch->last_scan == RISCV_SCAN_TYPE_NOP); + return riscv_batch_was_scan_busy(batch, batch->used_scans - 1); +} + +size_t riscv_batch_finished_scans(const struct riscv_batch *batch) +{ + if (!riscv_batch_was_batch_busy(batch)) { + /* Whole batch succeeded. */ + return batch->used_scans; + } + assert(batch->used_scans); + size_t first_busy = 0; + while (!riscv_batch_was_scan_busy(batch, first_busy)) + ++first_busy; + return first_busy; } diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h index 537fa5923..5d8b57234 100644 --- a/src/target/riscv/batch.h +++ b/src/target/riscv/batch.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef TARGET__RISCV__SCANS_H -#define TARGET__RISCV__SCANS_H +#ifndef OPENOCD_TARGET_RISCV_BATCH_H +#define OPENOCD_TARGET_RISCV_BATCH_H #include "target/target.h" #include "jtag/jtag.h" @@ -14,6 +14,112 @@ enum riscv_scan_type { RISCV_SCAN_TYPE_WRITE, }; +/* These types are used to specify how many JTAG RTI cycles to add after a + * scan. + */ +enum riscv_scan_delay_class { + /* Delay needed for accessing debug module registers: */ + RISCV_DELAY_BASE, + /* Delay for execution of an abstract command: */ + RISCV_DELAY_ABSTRACT_COMMAND, + /* Delay for System Bus read operation: */ + RISCV_DELAY_SYSBUS_READ, + /* Delay for System Bus write operation: */ + RISCV_DELAY_SYSBUS_WRITE +}; + +static inline const char * +riscv_scan_delay_class_name(enum riscv_scan_delay_class delay_class) +{ + switch (delay_class) { + case RISCV_DELAY_BASE: + return "DM access"; + case RISCV_DELAY_ABSTRACT_COMMAND: + return "Abstract Command"; + case RISCV_DELAY_SYSBUS_READ: + return "System Bus read"; + case RISCV_DELAY_SYSBUS_WRITE: + return "System Bus write"; + } + assert(0); + return NULL; +} + +/* The scan delay values are passed to "jtag_add_runtest()", which accepts an + * "int". Therefore, the passed value should be no greater than "INT_MAX". + * + * Since the resulting delay value can be a sum of two individual delays, + * individual delays are limited to "INT_MAX / 2" to prevent overflow of the + * final sum. + */ +#define RISCV_SCAN_DELAY_MAX (INT_MAX / 2) + +struct riscv_scan_delays { + unsigned int base_delay; + unsigned int ac_delay; + unsigned int sb_read_delay; + unsigned int sb_write_delay; +}; + +static inline unsigned int +riscv_scan_get_delay(const struct riscv_scan_delays *delays, + enum riscv_scan_delay_class delay_class) +{ + switch (delay_class) { + case RISCV_DELAY_BASE: + return delays->base_delay; + case RISCV_DELAY_ABSTRACT_COMMAND: + return delays->base_delay + delays->ac_delay; + case RISCV_DELAY_SYSBUS_READ: + return delays->base_delay + delays->sb_read_delay; + case RISCV_DELAY_SYSBUS_WRITE: + return delays->base_delay + delays->sb_write_delay; + } + assert(0); + return 0; +} + +static inline void riscv_scan_set_delay(struct riscv_scan_delays *delays, + enum riscv_scan_delay_class delay_class, unsigned int delay) +{ + assert(delay <= RISCV_SCAN_DELAY_MAX); + LOG_DEBUG("%s delay is set to %u.", + riscv_scan_delay_class_name(delay_class), delay); + switch (delay_class) { + case RISCV_DELAY_BASE: + delays->base_delay = delay; + return; + case RISCV_DELAY_ABSTRACT_COMMAND: + delays->ac_delay = delay; + return; + case RISCV_DELAY_SYSBUS_READ: + delays->sb_read_delay = delay; + return; + case RISCV_DELAY_SYSBUS_WRITE: + delays->sb_write_delay = delay; + return; + } + assert(0); +} + +static inline int riscv_scan_increase_delay(struct riscv_scan_delays *delays, + enum riscv_scan_delay_class delay_class) +{ + const unsigned int delay = riscv_scan_get_delay(delays, delay_class); + const unsigned int delay_step = delay / 10 + 1; + if (delay > RISCV_SCAN_DELAY_MAX - delay_step) { + /* It's not clear if this issue actually occurs in real + * use-cases, so stick with a simple solution until the + * first bug report. + */ + LOG_ERROR("Delay for %s (%d) is not increased anymore (maximum was reached).", + riscv_scan_delay_class_name(delay_class), delay); + return ERROR_FAIL; + } + riscv_scan_set_delay(delays, delay_class, delay + delay_step); + return ERROR_OK; +} + /* A batch of multiple JTAG scans, which are grouped together to avoid the * overhead of some JTAG adapters when sending single commands. This is * designed to support block copies, as that's what we actually need to go @@ -24,11 +130,10 @@ struct riscv_batch { size_t allocated_scans; size_t used_scans; - size_t idle_count; - uint8_t *data_out; uint8_t *data_in; struct scan_field *fields; + enum riscv_scan_delay_class *delay_classes; /* If in BSCAN mode, this field will be allocated (one per scan), and utilized to tunnel all the scans in the batch. If not in @@ -44,29 +149,75 @@ struct riscv_batch { /* The read keys. */ size_t *read_keys; size_t read_keys_used; + + /* Flag indicating that the last run of the batch finished without an error + * from the underlying JTAG layer of OpenOCD - all scans were performed. + * However, RISC-V DMI "busy" condition could still have occurred. + */ + bool was_run; + /* Number of RTI cycles used by the last scan on the last run. + * Only valid when `was_run` is set. + */ + unsigned int last_scan_delay; }; /* Allocates (or frees) a new scan set. "scans" is the maximum number of JTAG - * scans that can be issued to this object, and idle is the number of JTAG idle - * cycles between every real scan. */ -struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle); + * scans that can be issued to this object. */ +struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans); void riscv_batch_free(struct riscv_batch *batch); /* Checks to see if this batch is full. */ bool riscv_batch_full(struct riscv_batch *batch); -/* Executes this scan batch. */ -int riscv_batch_run(struct riscv_batch *batch); +/* Executes this batch of JTAG DTM DMI scans, starting form "start" scan. + * + * If batch is run for the first time, it is expected that "start" is zero. + * It is expected that the batch ends with a DMI NOP operation. + * + * "idle_counts" specifies the number of JTAG Run-Test-Idle cycles to add + * after each scan depending on the delay class of the scan. + * + * If "resets_delays" is true, the algorithm will stop inserting idle cycles + * (JTAG Run-Test-Idle) after "reset_delays_after" number of scans is + * performed. This is useful for stress-testing of RISC-V algorithms in + * OpenOCD that are based on batches. + */ +int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx, + const struct riscv_scan_delays *delays, bool resets_delays, + size_t reset_delays_after); -/* Adds a DMI write to this batch. */ -void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned int address, uint64_t data); +/* Get the number of scans successfully executed form this batch. */ +size_t riscv_batch_finished_scans(const struct riscv_batch *batch); -/* DMI reads must be handled in two parts: the first one schedules a read and +/* Adds a DM register write to this batch. */ +void riscv_batch_add_dmi_write(struct riscv_batch *batch, uint32_t address, uint32_t data, + bool read_back, enum riscv_scan_delay_class delay_class); + +static inline void +riscv_batch_add_dm_write(struct riscv_batch *batch, uint32_t address, uint32_t data, + bool read_back, enum riscv_scan_delay_class delay_type) +{ + return riscv_batch_add_dmi_write(batch, + riscv_get_dmi_address(batch->target, address), data, + read_back, delay_type); +} + +/* DM register reads must be handled in two parts: the first one schedules a read and * provides a key, the second one actually obtains the result of the read - * status (op) and the actual data. */ -size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned int address); -uint32_t riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key); -uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key); +size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, uint32_t address, + enum riscv_scan_delay_class delay_class); + +static inline size_t +riscv_batch_add_dm_read(struct riscv_batch *batch, uint32_t address, + enum riscv_scan_delay_class delay_type) +{ + return riscv_batch_add_dmi_read(batch, + riscv_get_dmi_address(batch->target, address), delay_type); +} + +uint32_t riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key); +uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key); /* Scans in a NOP. */ void riscv_batch_add_nop(struct riscv_batch *batch); @@ -74,4 +225,7 @@ void riscv_batch_add_nop(struct riscv_batch *batch); /* Returns the number of available scans. */ size_t riscv_batch_available_scans(struct riscv_batch *batch); -#endif +/* Return true iff the last scan in the batch returned DMI_OP_BUSY. */ +bool riscv_batch_was_batch_busy(const struct riscv_batch *batch); + +#endif /* OPENOCD_TARGET_RISCV_BATCH_H */ diff --git a/src/target/riscv/debug_defines.c b/src/target/riscv/debug_defines.c new file mode 100644 index 000000000..81644b2c2 --- /dev/null +++ b/src/target/riscv/debug_defines.c @@ -0,0 +1,3902 @@ +// SPDX-License-Identifier: BSD-2-Clause OR CC-BY-4.0 +/* This file was auto-generated by running 'make debug_defines' in https://github.com/riscv/riscv-debug-spec/ (22a7576) */ + +#include "debug_defines.h" +#include +#include +static riscv_debug_reg_field_list_t dtm_idcode_get_version(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "Version", + .lsb = 0x1c, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_idcode_get_partnumber(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "PartNumber", + .lsb = 0xc, + .msb = 0x1b, + .values = NULL + }, + .get_next = dtm_idcode_get_version + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_idcode_get_manufid(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ManufId", + .lsb = 1, + .msb = 0xb, + .values = NULL + }, + .get_next = dtm_idcode_get_partnumber + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_idcode_get_1(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "1", + .lsb = 0, + .msb = 0, + .values = NULL + }, + .get_next = dtm_idcode_get_manufid + }; + return result; +} + +static const char *dtm_dtmcs_errinfo_values[8] = { + [0] = "not_implemented", + [1] = "dmi_error", + [2] = "communication_error", + [3] = "device_error", + [4] = "unknown" +}; +static const char *dtm_dtmcs_version_values[16] = { + [0] = "0_11", + [1] = "1_0", + [15] = "custom" +}; +static riscv_debug_reg_field_list_t dtm_dtmcs_get_abits(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "abits", + .lsb = 4, + .msb = 9, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_errinfo(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "errinfo", + .lsb = 0x12, + .msb = 0x14, + .values = dtm_dtmcs_errinfo_values + }, + .get_next = dtm_dtmcs_get_abits + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_dtmhardreset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dtmhardreset", + .lsb = 0x11, + .msb = 0x11, + .values = NULL + }, + .get_next = dtm_dtmcs_get_errinfo + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_dmireset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmireset", + .lsb = 0x10, + .msb = 0x10, + .values = NULL + }, + .get_next = dtm_dtmcs_get_dtmhardreset + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_idle(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "idle", + .lsb = 0xc, + .msb = 0xe, + .values = NULL + }, + .get_next = dtm_dtmcs_get_dmireset + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_dmistat(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmistat", + .lsb = 0xa, + .msb = 0xb, + .values = NULL + }, + .get_next = dtm_dtmcs_get_idle + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dtmcs_get_version(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "version", + .lsb = 0, + .msb = 3, + .values = dtm_dtmcs_version_values + }, + .get_next = dtm_dtmcs_get_dmistat + }; + return result; +} + +static const char *dtm_dmi_op_values[4] = {}; +static riscv_debug_reg_field_list_t dtm_dmi_get_address(riscv_debug_reg_ctx_t context) +{ + assert(context.abits.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0x22, + .msb = (context.abits.value + 0x21), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dmi_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 2, + .msb = 0x21, + .values = NULL + }, + .get_next = dtm_dmi_get_address + }; + return result; +} + +static riscv_debug_reg_field_list_t dtm_dmi_get_op(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "op", + .lsb = 0, + .msb = 1, + .values = dtm_dmi_op_values + }, + .get_next = dtm_dmi_get_data + }; + return result; +} + + +static const char *csr_dcsr_debugver_values[16] = { + [0] = "none", + [4] = "1_0", + [15] = "custom" +}; +static const char *csr_dcsr_extcause_values[8] = { + [0] = "critical_error" +}; +static const char *csr_dcsr_cetrig_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *csr_dcsr_pelp_values[2] = { + [0] = "NO_LP_EXPECTED", + [1] = "LP_EXPECTED" +}; +static const char *csr_dcsr_ebreakvs_values[2] = { + [0] = "exception", + [1] = "debug_mode" +}; +static const char *csr_dcsr_ebreakvu_values[2] = { + [0] = "exception", + [1] = "debug_mode" +}; +static const char *csr_dcsr_ebreakm_values[2] = { + [0] = "exception", + [1] = "debug_mode" +}; +static const char *csr_dcsr_ebreaks_values[2] = { + [0] = "exception", + [1] = "debug_mode" +}; +static const char *csr_dcsr_ebreaku_values[2] = { + [0] = "exception", + [1] = "debug_mode" +}; +static const char *csr_dcsr_stepie_values[2] = { + [0] = "interrupts_disabled", + [1] = "interrupts_enabled" +}; +static const char *csr_dcsr_stopcount_values[2] = { + [0] = "normal", + [1] = "freeze" +}; +static const char *csr_dcsr_stoptime_values[2] = { + [0] = "normal", + [1] = "freeze" +}; +static const char *csr_dcsr_cause_values[8] = { + [1] = "ebreak", + [2] = "trigger", + [3] = "haltreq", + [4] = "step", + [5] = "resethaltreq", + [6] = "group", + [7] = "other" +}; +static const char *csr_dcsr_mprven_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static riscv_debug_reg_field_list_t csr_dcsr_get_stoptime(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "stoptime", + .lsb = 9, + .msb = 9, + .values = csr_dcsr_stoptime_values + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_cause(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cause", + .lsb = 6, + .msb = 8, + .values = csr_dcsr_cause_values + }, + .get_next = csr_dcsr_get_stoptime + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_v(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "v", + .lsb = 5, + .msb = 5, + .values = NULL + }, + .get_next = csr_dcsr_get_cause + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_mprven(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mprven", + .lsb = 4, + .msb = 4, + .values = csr_dcsr_mprven_values + }, + .get_next = csr_dcsr_get_v + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_nmip(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "nmip", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = csr_dcsr_get_mprven + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_debugver(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "debugver", + .lsb = 0x1c, + .msb = 0x1f, + .values = csr_dcsr_debugver_values + }, + .get_next = csr_dcsr_get_nmip + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_extcause(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "extcause", + .lsb = 0x18, + .msb = 0x1a, + .values = csr_dcsr_extcause_values + }, + .get_next = csr_dcsr_get_debugver + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_step(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "step", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = csr_dcsr_get_extcause + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_cetrig(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cetrig", + .lsb = 0x13, + .msb = 0x13, + .values = csr_dcsr_cetrig_values + }, + .get_next = csr_dcsr_get_step + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_pelp(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "pelp", + .lsb = 0x12, + .msb = 0x12, + .values = csr_dcsr_pelp_values + }, + .get_next = csr_dcsr_get_cetrig + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreakvs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreakvs", + .lsb = 0x11, + .msb = 0x11, + .values = csr_dcsr_ebreakvs_values + }, + .get_next = csr_dcsr_get_pelp + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreakvu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreakvu", + .lsb = 0x10, + .msb = 0x10, + .values = csr_dcsr_ebreakvu_values + }, + .get_next = csr_dcsr_get_ebreakvs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreakm(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreakm", + .lsb = 0xf, + .msb = 0xf, + .values = csr_dcsr_ebreakm_values + }, + .get_next = csr_dcsr_get_ebreakvu + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreaks(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreaks", + .lsb = 0xd, + .msb = 0xd, + .values = csr_dcsr_ebreaks_values + }, + .get_next = csr_dcsr_get_ebreakm + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_ebreaku(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ebreaku", + .lsb = 0xc, + .msb = 0xc, + .values = csr_dcsr_ebreaku_values + }, + .get_next = csr_dcsr_get_ebreaks + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_stepie(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "stepie", + .lsb = 0xb, + .msb = 0xb, + .values = csr_dcsr_stepie_values + }, + .get_next = csr_dcsr_get_ebreaku + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_stopcount(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "stopcount", + .lsb = 0xa, + .msb = 0xa, + .values = csr_dcsr_stopcount_values + }, + .get_next = csr_dcsr_get_stepie + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dcsr_get_prv(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "prv", + .lsb = 0, + .msb = 1, + .values = NULL + }, + .get_next = csr_dcsr_get_stopcount + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dpc_get_dpc(riscv_debug_reg_ctx_t context) +{ + assert(context.DXLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dpc", + .lsb = 0, + .msb = (context.DXLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dscratch0_get_dscratch0(riscv_debug_reg_ctx_t context) +{ + assert(context.DXLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dscratch0", + .lsb = 0, + .msb = (context.DXLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_dscratch1_get_dscratch1(riscv_debug_reg_ctx_t context) +{ + assert(context.DXLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dscratch1", + .lsb = 0, + .msb = (context.DXLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tselect_get_index(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "index", + .lsb = 0, + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *csr_tdata1_type_values[16] = { + [0] = "none", + [1] = "legacy", + [2] = "mcontrol", + [3] = "icount", + [4] = "itrigger", + [5] = "etrigger", + [6] = "mcontrol6", + [7] = "tmexttrigger", + [15] = "disabled" +}; +static const char *csr_tdata1_dmode_values[2] = { + [0] = "both", + [1] = "dmode" +}; +static riscv_debug_reg_field_list_t csr_tdata1_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = csr_tdata1_dmode_values + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tdata1_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = csr_tdata1_type_values + }, + .get_next = csr_tdata1_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tdata1_get_data(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = csr_tdata1_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tdata2_get_data(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tdata3_get_data(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *csr_tinfo_version_values[256] = { + [0] = "0", + [1] = "1" +}; +static riscv_debug_reg_field_list_t csr_tinfo_get_version(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "version", + .lsb = 0x18, + .msb = 0x1f, + .values = csr_tinfo_version_values + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tinfo_get_info(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "info", + .lsb = 0, + .msb = 0xf, + .values = NULL + }, + .get_next = csr_tinfo_get_version + }; + return result; +} + +static const char *csr_tcontrol_mte_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static riscv_debug_reg_field_list_t csr_tcontrol_get_mpte(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mpte", + .lsb = 7, + .msb = 7, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tcontrol_get_mte(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mte", + .lsb = 3, + .msb = 3, + .values = csr_tcontrol_mte_values + }, + .get_next = csr_tcontrol_get_mpte + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_scontext_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontext_get_hcontext(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hcontext", + .lsb = 0, + .msb = 0xd, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *csr_mcontrol_select_values[2] = { + [0] = "address", + [1] = "data" +}; +static const char *csr_mcontrol_timing_values[2] = { + [0] = "before", + [1] = "after" +}; +static const char *csr_mcontrol_sizelo_values[4] = {}; +static const char *csr_mcontrol_action_values[16] = { + [0] = "breakpoint", + [1] = "debug_mode", + [2] = "trace_on", + [3] = "trace_off", + [4] = "trace_notify", + [8] = "external0", + [9] = "external1" +}; +static const char *csr_mcontrol_chain_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *csr_mcontrol_match_values[16] = { + [0] = "equal", + [1] = "napot", + [2] = "ge", + [3] = "lt", + [4] = "mask_low", + [5] = "mask_high", + [8] = "not_equal", + [9] = "not_napot", + [12] = "not_mask_low", + [13] = "not_mask_high" +}; +static riscv_debug_reg_field_list_t csr_mcontrol_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_mcontrol_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_maskmax(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "maskmax", + .lsb = (context.XLEN.value + -0xb), + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = csr_mcontrol_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_match(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "match", + .lsb = 7, + .msb = 0xa, + .values = csr_mcontrol_match_values + }, + .get_next = csr_mcontrol_get_maskmax + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_mcontrol_get_match + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 4, + .msb = 4, + .values = NULL + }, + .get_next = csr_mcontrol_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = csr_mcontrol_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_sizehi(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sizehi", + .lsb = 0x15, + .msb = 0x16, + .values = NULL + }, + .get_next = csr_mcontrol_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_hit(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = 0x14, + .msb = 0x14, + .values = NULL + }, + .get_next = csr_mcontrol_get_sizehi + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_execute(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "execute", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = csr_mcontrol_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_select(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "select", + .lsb = 0x13, + .msb = 0x13, + .values = csr_mcontrol_select_values + }, + .get_next = csr_mcontrol_get_execute + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_timing(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "timing", + .lsb = 0x12, + .msb = 0x12, + .values = csr_mcontrol_timing_values + }, + .get_next = csr_mcontrol_get_select + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_sizelo(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sizelo", + .lsb = 0x10, + .msb = 0x11, + .values = csr_mcontrol_sizelo_values + }, + .get_next = csr_mcontrol_get_timing + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0xc, + .msb = 0xf, + .values = csr_mcontrol_action_values + }, + .get_next = csr_mcontrol_get_sizelo + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_chain(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "chain", + .lsb = 0xb, + .msb = 0xb, + .values = csr_mcontrol_chain_values + }, + .get_next = csr_mcontrol_get_action + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_store(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "store", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = csr_mcontrol_get_chain + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol_get_load(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "load", + .lsb = 0, + .msb = 0, + .values = NULL + }, + .get_next = csr_mcontrol_get_store + }; + return result; +} + +static const char *csr_mcontrol6_uncertain_values[2] = { + [0] = "certain", + [1] = "uncertain" +}; +static const char *csr_mcontrol6_hit0_values[2] = {}; +static const char *csr_mcontrol6_select_values[2] = { + [0] = "address", + [1] = "data" +}; +static const char *csr_mcontrol6_size_values[8] = { + [0] = "any", + [1] = "8bit", + [2] = "16bit", + [3] = "32bit", + [4] = "48bit", + [5] = "64bit", + [6] = "128bit" +}; +static const char *csr_mcontrol6_action_values[16] = { + [0] = "breakpoint", + [1] = "debug_mode", + [2] = "trace_on", + [3] = "trace_off", + [4] = "trace_notify", + [8] = "external0", + [9] = "external1" +}; +static const char *csr_mcontrol6_chain_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *csr_mcontrol6_match_values[16] = { + [0] = "equal", + [1] = "napot", + [2] = "ge", + [3] = "lt", + [4] = "mask_low", + [5] = "mask_high", + [8] = "not_equal", + [9] = "not_napot", + [12] = "not_mask_low", + [13] = "not_mask_high" +}; +static const char *csr_mcontrol6_uncertainen_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static riscv_debug_reg_field_list_t csr_mcontrol6_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_mcontrol6_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_match(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "match", + .lsb = 7, + .msb = 0xa, + .values = csr_mcontrol6_match_values + }, + .get_next = csr_mcontrol6_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_mcontrol6_get_match + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_uncertainen(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "uncertainen", + .lsb = 5, + .msb = 5, + .values = csr_mcontrol6_uncertainen_values + }, + .get_next = csr_mcontrol6_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 4, + .msb = 4, + .values = NULL + }, + .get_next = csr_mcontrol6_get_uncertainen + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = csr_mcontrol6_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_uncertain(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "uncertain", + .lsb = 0x1a, + .msb = 0x1a, + .values = csr_mcontrol6_uncertain_values + }, + .get_next = csr_mcontrol6_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_hit1(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit1", + .lsb = 0x19, + .msb = 0x19, + .values = NULL + }, + .get_next = csr_mcontrol6_get_uncertain + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_vs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vs", + .lsb = 0x18, + .msb = 0x18, + .values = NULL + }, + .get_next = csr_mcontrol6_get_hit1 + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_vu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vu", + .lsb = 0x17, + .msb = 0x17, + .values = NULL + }, + .get_next = csr_mcontrol6_get_vs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_hit0(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit0", + .lsb = 0x16, + .msb = 0x16, + .values = csr_mcontrol6_hit0_values + }, + .get_next = csr_mcontrol6_get_vu + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_select(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "select", + .lsb = 0x15, + .msb = 0x15, + .values = csr_mcontrol6_select_values + }, + .get_next = csr_mcontrol6_get_hit0 + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_execute(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "execute", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = csr_mcontrol6_get_select + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_size(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "size", + .lsb = 0x10, + .msb = 0x12, + .values = csr_mcontrol6_size_values + }, + .get_next = csr_mcontrol6_get_execute + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0xc, + .msb = 0xf, + .values = csr_mcontrol6_action_values + }, + .get_next = csr_mcontrol6_get_size + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_chain(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "chain", + .lsb = 0xb, + .msb = 0xb, + .values = csr_mcontrol6_chain_values + }, + .get_next = csr_mcontrol6_get_action + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_store(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "store", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = csr_mcontrol6_get_chain + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_mcontrol6_get_load(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "load", + .lsb = 0, + .msb = 0, + .values = NULL + }, + .get_next = csr_mcontrol6_get_store + }; + return result; +} + +static const char *csr_icount_action_values[64] = { + [0] = "breakpoint", + [1] = "debug_mode", + [2] = "trace_on", + [3] = "trace_off", + [4] = "trace_notify", + [8] = "external0", + [9] = "external1" +}; +static riscv_debug_reg_field_list_t csr_icount_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_icount_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 9, + .msb = 9, + .values = NULL + }, + .get_next = csr_icount_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_pending(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "pending", + .lsb = 8, + .msb = 8, + .values = NULL + }, + .get_next = csr_icount_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 7, + .msb = 7, + .values = NULL + }, + .get_next = csr_icount_get_pending + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_icount_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_vs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vs", + .lsb = 0x1a, + .msb = 0x1a, + .values = NULL + }, + .get_next = csr_icount_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_vu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vu", + .lsb = 0x19, + .msb = 0x19, + .values = NULL + }, + .get_next = csr_icount_get_vs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_hit(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = 0x18, + .msb = 0x18, + .values = NULL + }, + .get_next = csr_icount_get_vu + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_count(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "count", + .lsb = 0xa, + .msb = 0x17, + .values = NULL + }, + .get_next = csr_icount_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_icount_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0, + .msb = 5, + .values = csr_icount_action_values + }, + .get_next = csr_icount_get_count + }; + return result; +} + +static const char *csr_itrigger_action_values[64] = { + [0] = "breakpoint", + [1] = "debug_mode", + [2] = "trace_on", + [3] = "trace_off", + [4] = "trace_notify", + [8] = "external0", + [9] = "external1" +}; +static riscv_debug_reg_field_list_t csr_itrigger_get_hit(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = (context.XLEN.value + -6), + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = csr_itrigger_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_itrigger_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 9, + .msb = 9, + .values = NULL + }, + .get_next = csr_itrigger_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 7, + .msb = 7, + .values = NULL + }, + .get_next = csr_itrigger_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_itrigger_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_vs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vs", + .lsb = 0xc, + .msb = 0xc, + .values = NULL + }, + .get_next = csr_itrigger_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_vu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vu", + .lsb = 0xb, + .msb = 0xb, + .values = NULL + }, + .get_next = csr_itrigger_get_vs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_nmi(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "nmi", + .lsb = 0xa, + .msb = 0xa, + .values = NULL + }, + .get_next = csr_itrigger_get_vu + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_itrigger_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0, + .msb = 5, + .values = csr_itrigger_action_values + }, + .get_next = csr_itrigger_get_nmi + }; + return result; +} + +static const char *csr_etrigger_action_values[64] = { + [0] = "breakpoint", + [1] = "debug_mode", + [2] = "trace_on", + [3] = "trace_off", + [4] = "trace_notify", + [8] = "external0", + [9] = "external1" +}; +static riscv_debug_reg_field_list_t csr_etrigger_get_hit(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = (context.XLEN.value + -6), + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = csr_etrigger_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_etrigger_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_m(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "m", + .lsb = 9, + .msb = 9, + .values = NULL + }, + .get_next = csr_etrigger_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_s(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "s", + .lsb = 7, + .msb = 7, + .values = NULL + }, + .get_next = csr_etrigger_get_m + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_u(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "u", + .lsb = 6, + .msb = 6, + .values = NULL + }, + .get_next = csr_etrigger_get_s + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_vs(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vs", + .lsb = 0xc, + .msb = 0xc, + .values = NULL + }, + .get_next = csr_etrigger_get_u + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_vu(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "vu", + .lsb = 0xb, + .msb = 0xb, + .values = NULL + }, + .get_next = csr_etrigger_get_vs + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_etrigger_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0, + .msb = 5, + .values = csr_etrigger_action_values + }, + .get_next = csr_etrigger_get_vu + }; + return result; +} + +static const char *csr_tmexttrigger_action_values[64] = { + [0] = "breakpoint", + [1] = "debug_mode", + [2] = "trace_on", + [3] = "trace_off", + [4] = "trace_notify", + [8] = "external0", + [9] = "external1" +}; +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_hit(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hit", + .lsb = (context.XLEN.value + -6), + .msb = (context.XLEN.value + -6), + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_dmode(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmode", + .lsb = (context.XLEN.value + -5), + .msb = (context.XLEN.value + -5), + .values = NULL + }, + .get_next = csr_tmexttrigger_get_hit + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_type(riscv_debug_reg_ctx_t context) +{ + assert(context.XLEN.is_set); + riscv_debug_reg_field_list_t result = { + .field = { + .name = "type", + .lsb = (context.XLEN.value + -4), + .msb = (context.XLEN.value + -1), + .values = NULL + }, + .get_next = csr_tmexttrigger_get_dmode + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_select(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "select", + .lsb = 6, + .msb = 0x15, + .values = NULL + }, + .get_next = csr_tmexttrigger_get_type + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_intctl(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "intctl", + .lsb = 0x16, + .msb = 0x16, + .values = NULL + }, + .get_next = csr_tmexttrigger_get_select + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_tmexttrigger_get_action(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "action", + .lsb = 0, + .msb = 5, + .values = csr_tmexttrigger_action_values + }, + .get_next = csr_tmexttrigger_get_intctl + }; + return result; +} + +static const char *csr_textra32_mhselect_values[8] = { + [0] = "ignore", + [4] = "mcontext" +}; +static const char *csr_textra32_sselect_values[4] = { + [0] = "ignore", + [1] = "scontext", + [2] = "asid" +}; +static riscv_debug_reg_field_list_t csr_textra32_get_mhvalue(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mhvalue", + .lsb = 0x1a, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra32_get_mhselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mhselect", + .lsb = 0x17, + .msb = 0x19, + .values = csr_textra32_mhselect_values + }, + .get_next = csr_textra32_get_mhvalue + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra32_get_svalue(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "svalue", + .lsb = 2, + .msb = 0x11, + .values = NULL + }, + .get_next = csr_textra32_get_mhselect + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra32_get_sbytemask(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbytemask", + .lsb = 0x12, + .msb = 0x13, + .values = NULL + }, + .get_next = csr_textra32_get_svalue + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra32_get_sselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sselect", + .lsb = 0, + .msb = 1, + .values = csr_textra32_sselect_values + }, + .get_next = csr_textra32_get_sbytemask + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_mhvalue(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mhvalue", + .lsb = 0x33, + .msb = 0x3f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_mhselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "mhselect", + .lsb = 0x30, + .msb = 0x32, + .values = NULL + }, + .get_next = csr_textra64_get_mhvalue + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_sbytemask(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbytemask", + .lsb = 0x24, + .msb = 0x27, + .values = NULL + }, + .get_next = csr_textra64_get_mhselect + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_svalue(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "svalue", + .lsb = 2, + .msb = 0x21, + .values = NULL + }, + .get_next = csr_textra64_get_sbytemask + }; + return result; +} + +static riscv_debug_reg_field_list_t csr_textra64_get_sselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sselect", + .lsb = 0, + .msb = 1, + .values = NULL + }, + .get_next = csr_textra64_get_svalue + }; + return result; +} + +static const char *dm_dmstatus_ndmresetpending_values[2] = { + [0] = "false", + [1] = "true" +}; +static const char *dm_dmstatus_stickyunavail_values[2] = { + [0] = "current", + [1] = "sticky" +}; +static const char *dm_dmstatus_authenticated_values[2] = { + [0] = "false", + [1] = "true" +}; +static const char *dm_dmstatus_authbusy_values[2] = { + [0] = "ready", + [1] = "busy" +}; +static const char *dm_dmstatus_confstrptrvalid_values[2] = { + [0] = "invalid", + [1] = "valid" +}; +static const char *dm_dmstatus_version_values[16] = { + [0] = "none", + [1] = "0_11", + [2] = "0_13", + [3] = "1_0", + [15] = "custom" +}; +static riscv_debug_reg_field_list_t dm_dmstatus_get_allhalted(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allhalted", + .lsb = 9, + .msb = 9, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyhalted(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyhalted", + .lsb = 8, + .msb = 8, + .values = NULL + }, + .get_next = dm_dmstatus_get_allhalted + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_authenticated(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "authenticated", + .lsb = 7, + .msb = 7, + .values = dm_dmstatus_authenticated_values + }, + .get_next = dm_dmstatus_get_anyhalted + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_authbusy(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "authbusy", + .lsb = 6, + .msb = 6, + .values = dm_dmstatus_authbusy_values + }, + .get_next = dm_dmstatus_get_authenticated + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_hasresethaltreq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hasresethaltreq", + .lsb = 5, + .msb = 5, + .values = NULL + }, + .get_next = dm_dmstatus_get_authbusy + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_confstrptrvalid(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "confstrptrvalid", + .lsb = 4, + .msb = 4, + .values = dm_dmstatus_confstrptrvalid_values + }, + .get_next = dm_dmstatus_get_hasresethaltreq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_ndmresetpending(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ndmresetpending", + .lsb = 0x18, + .msb = 0x18, + .values = dm_dmstatus_ndmresetpending_values + }, + .get_next = dm_dmstatus_get_confstrptrvalid + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_stickyunavail(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "stickyunavail", + .lsb = 0x17, + .msb = 0x17, + .values = dm_dmstatus_stickyunavail_values + }, + .get_next = dm_dmstatus_get_ndmresetpending + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_impebreak(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "impebreak", + .lsb = 0x16, + .msb = 0x16, + .values = NULL + }, + .get_next = dm_dmstatus_get_stickyunavail + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allhavereset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allhavereset", + .lsb = 0x13, + .msb = 0x13, + .values = NULL + }, + .get_next = dm_dmstatus_get_impebreak + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyhavereset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyhavereset", + .lsb = 0x12, + .msb = 0x12, + .values = NULL + }, + .get_next = dm_dmstatus_get_allhavereset + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allresumeack(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allresumeack", + .lsb = 0x11, + .msb = 0x11, + .values = NULL + }, + .get_next = dm_dmstatus_get_anyhavereset + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyresumeack(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyresumeack", + .lsb = 0x10, + .msb = 0x10, + .values = NULL + }, + .get_next = dm_dmstatus_get_allresumeack + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allnonexistent(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allnonexistent", + .lsb = 0xf, + .msb = 0xf, + .values = NULL + }, + .get_next = dm_dmstatus_get_anyresumeack + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anynonexistent(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anynonexistent", + .lsb = 0xe, + .msb = 0xe, + .values = NULL + }, + .get_next = dm_dmstatus_get_allnonexistent + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allunavail(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allunavail", + .lsb = 0xd, + .msb = 0xd, + .values = NULL + }, + .get_next = dm_dmstatus_get_anynonexistent + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyunavail(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyunavail", + .lsb = 0xc, + .msb = 0xc, + .values = NULL + }, + .get_next = dm_dmstatus_get_allunavail + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_allrunning(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "allrunning", + .lsb = 0xb, + .msb = 0xb, + .values = NULL + }, + .get_next = dm_dmstatus_get_anyunavail + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_anyrunning(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "anyrunning", + .lsb = 0xa, + .msb = 0xa, + .values = NULL + }, + .get_next = dm_dmstatus_get_allrunning + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmstatus_get_version(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "version", + .lsb = 0, + .msb = 3, + .values = dm_dmstatus_version_values + }, + .get_next = dm_dmstatus_get_anyrunning + }; + return result; +} + +static const char *dm_dmcontrol_ackhavereset_values[2] = { + [0] = "nop", + [1] = "ack" +}; +static const char *dm_dmcontrol_ackunavail_values[2] = { + [0] = "nop", + [1] = "ack" +}; +static const char *dm_dmcontrol_hasel_values[2] = { + [0] = "single", + [1] = "multiple" +}; +static const char *dm_dmcontrol_dmactive_values[2] = { + [0] = "inactive", + [1] = "active" +}; +static riscv_debug_reg_field_list_t dm_dmcontrol_get_hartselhi(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hartselhi", + .lsb = 6, + .msb = 0xf, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_setkeepalive(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "setkeepalive", + .lsb = 5, + .msb = 5, + .values = NULL + }, + .get_next = dm_dmcontrol_get_hartselhi + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_clrkeepalive(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "clrkeepalive", + .lsb = 4, + .msb = 4, + .values = NULL + }, + .get_next = dm_dmcontrol_get_setkeepalive + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_haltreq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltreq", + .lsb = 0x1f, + .msb = 0x1f, + .values = NULL + }, + .get_next = dm_dmcontrol_get_clrkeepalive + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_resumereq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "resumereq", + .lsb = 0x1e, + .msb = 0x1e, + .values = NULL + }, + .get_next = dm_dmcontrol_get_haltreq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_setresethaltreq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "setresethaltreq", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = dm_dmcontrol_get_resumereq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_hartreset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hartreset", + .lsb = 0x1d, + .msb = 0x1d, + .values = NULL + }, + .get_next = dm_dmcontrol_get_setresethaltreq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_ackhavereset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ackhavereset", + .lsb = 0x1c, + .msb = 0x1c, + .values = dm_dmcontrol_ackhavereset_values + }, + .get_next = dm_dmcontrol_get_hartreset + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_ackunavail(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ackunavail", + .lsb = 0x1b, + .msb = 0x1b, + .values = dm_dmcontrol_ackunavail_values + }, + .get_next = dm_dmcontrol_get_ackhavereset + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_hasel(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hasel", + .lsb = 0x1a, + .msb = 0x1a, + .values = dm_dmcontrol_hasel_values + }, + .get_next = dm_dmcontrol_get_ackunavail + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_clrresethaltreq(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "clrresethaltreq", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = dm_dmcontrol_get_hasel + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_hartsello(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hartsello", + .lsb = 0x10, + .msb = 0x19, + .values = NULL + }, + .get_next = dm_dmcontrol_get_clrresethaltreq + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_ndmreset(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "ndmreset", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = dm_dmcontrol_get_hartsello + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcontrol_get_dmactive(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmactive", + .lsb = 0, + .msb = 0, + .values = dm_dmcontrol_dmactive_values + }, + .get_next = dm_dmcontrol_get_ndmreset + }; + return result; +} + +static const char *dm_hartinfo_dataaccess_values[2] = { + [0] = "csr", + [1] = "memory" +}; +static riscv_debug_reg_field_list_t dm_hartinfo_get_nscratch(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "nscratch", + .lsb = 0x14, + .msb = 0x17, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hartinfo_get_dataaccess(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dataaccess", + .lsb = 0x10, + .msb = 0x10, + .values = dm_hartinfo_dataaccess_values + }, + .get_next = dm_hartinfo_get_nscratch + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hartinfo_get_datasize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "datasize", + .lsb = 0xc, + .msb = 0xf, + .values = NULL + }, + .get_next = dm_hartinfo_get_dataaccess + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hartinfo_get_dataaddr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dataaddr", + .lsb = 0, + .msb = 0xb, + .values = NULL + }, + .get_next = dm_hartinfo_get_datasize + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hawindowsel_get_hawindowsel(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hawindowsel", + .lsb = 0, + .msb = 0xe, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_hawindow_get_maskdata(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "maskdata", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *dm_abstractcs_busy_values[2] = { + [0] = "ready", + [1] = "busy" +}; +static const char *dm_abstractcs_relaxedpriv_values[2] = { + [0] = "full_checks", + [1] = "relaxed_checks" +}; +static const char *dm_abstractcs_cmderr_values[8] = { + [0] = "none", + [1] = "busy", + [2] = "not_supported", + [3] = "exception", + [4] = "halt_resume", + [5] = "bus", + [6] = "reserved", + [7] = "other" +}; +static riscv_debug_reg_field_list_t dm_abstractcs_get_cmderr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmderr", + .lsb = 8, + .msb = 0xa, + .values = dm_abstractcs_cmderr_values + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractcs_get_progbufsize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "progbufsize", + .lsb = 0x18, + .msb = 0x1c, + .values = NULL + }, + .get_next = dm_abstractcs_get_cmderr + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractcs_get_busy(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "busy", + .lsb = 0xc, + .msb = 0xc, + .values = dm_abstractcs_busy_values + }, + .get_next = dm_abstractcs_get_progbufsize + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractcs_get_relaxedpriv(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "relaxedpriv", + .lsb = 0xb, + .msb = 0xb, + .values = dm_abstractcs_relaxedpriv_values + }, + .get_next = dm_abstractcs_get_busy + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractcs_get_datacount(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "datacount", + .lsb = 0, + .msb = 3, + .values = NULL + }, + .get_next = dm_abstractcs_get_relaxedpriv + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_command_get_cmdtype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmdtype", + .lsb = 0x18, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_command_get_control(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "control", + .lsb = 0, + .msb = 0x17, + .values = NULL + }, + .get_next = dm_command_get_cmdtype + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractauto_get_autoexecprogbuf(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "autoexecprogbuf", + .lsb = 0x10, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_abstractauto_get_autoexecdata(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "autoexecdata", + .lsb = 0, + .msb = 0xb, + .values = NULL + }, + .get_next = dm_abstractauto_get_autoexecprogbuf + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_confstrptr0_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_confstrptr1_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_confstrptr2_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_confstrptr3_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_nextdm_get_addr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "addr", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_data0_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_progbuf0_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_authdata_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *dm_dmcs2_grouptype_values[2] = { + [0] = "halt", + [1] = "resume" +}; +static const char *dm_dmcs2_hgselect_values[2] = { + [0] = "harts", + [1] = "triggers" +}; +static riscv_debug_reg_field_list_t dm_dmcs2_get_dmexttrigger(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "dmexttrigger", + .lsb = 7, + .msb = 0xa, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcs2_get_group(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "group", + .lsb = 2, + .msb = 6, + .values = NULL + }, + .get_next = dm_dmcs2_get_dmexttrigger + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcs2_get_grouptype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "grouptype", + .lsb = 0xb, + .msb = 0xb, + .values = dm_dmcs2_grouptype_values + }, + .get_next = dm_dmcs2_get_group + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcs2_get_hgwrite(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hgwrite", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = dm_dmcs2_get_grouptype + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_dmcs2_get_hgselect(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "hgselect", + .lsb = 0, + .msb = 0, + .values = dm_dmcs2_hgselect_values + }, + .get_next = dm_dmcs2_get_hgwrite + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_haltsum0_get_haltsum0(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltsum0", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_haltsum1_get_haltsum1(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltsum1", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_haltsum2_get_haltsum2(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltsum2", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_haltsum3_get_haltsum3(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "haltsum3", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *dm_sbcs_sbversion_values[8] = { + [0] = "legacy", + [1] = "1_0" +}; +static const char *dm_sbcs_sbaccess_values[8] = { + [0] = "8bit", + [1] = "16bit", + [2] = "32bit", + [3] = "64bit", + [4] = "128bit" +}; +static const char *dm_sbcs_sberror_values[8] = { + [0] = "none", + [1] = "timeout", + [2] = "address", + [3] = "alignment", + [4] = "size", + [7] = "other" +}; +static riscv_debug_reg_field_list_t dm_sbcs_get_sbasize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbasize", + .lsb = 5, + .msb = 0xb, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess128(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess128", + .lsb = 4, + .msb = 4, + .values = NULL + }, + .get_next = dm_sbcs_get_sbasize + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess64(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess64", + .lsb = 3, + .msb = 3, + .values = NULL + }, + .get_next = dm_sbcs_get_sbaccess128 + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbversion(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbversion", + .lsb = 0x1d, + .msb = 0x1f, + .values = dm_sbcs_sbversion_values + }, + .get_next = dm_sbcs_get_sbaccess64 + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbbusyerror(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbbusyerror", + .lsb = 0x16, + .msb = 0x16, + .values = NULL + }, + .get_next = dm_sbcs_get_sbversion + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbbusy(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbbusy", + .lsb = 0x15, + .msb = 0x15, + .values = NULL + }, + .get_next = dm_sbcs_get_sbbusyerror + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbreadonaddr(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbreadonaddr", + .lsb = 0x14, + .msb = 0x14, + .values = NULL + }, + .get_next = dm_sbcs_get_sbbusy + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess32(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess32", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = dm_sbcs_get_sbreadonaddr + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess", + .lsb = 0x11, + .msb = 0x13, + .values = dm_sbcs_sbaccess_values + }, + .get_next = dm_sbcs_get_sbaccess32 + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbautoincrement(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbautoincrement", + .lsb = 0x10, + .msb = 0x10, + .values = NULL + }, + .get_next = dm_sbcs_get_sbaccess + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbreadondata(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbreadondata", + .lsb = 0xf, + .msb = 0xf, + .values = NULL + }, + .get_next = dm_sbcs_get_sbautoincrement + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sberror(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sberror", + .lsb = 0xc, + .msb = 0xe, + .values = dm_sbcs_sberror_values + }, + .get_next = dm_sbcs_get_sbreadondata + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess16(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess16", + .lsb = 1, + .msb = 1, + .values = NULL + }, + .get_next = dm_sbcs_get_sberror + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess8(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "sbaccess8", + .lsb = 0, + .msb = 0, + .values = NULL + }, + .get_next = dm_sbcs_get_sbaccess16 + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbaddress0_get_address(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbaddress1_get_address(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbaddress2_get_address(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbaddress3_get_address(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "address", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbdata0_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbdata1_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbdata2_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t dm_sbdata3_get_data(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "data", + .lsb = 0, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t shortname_get_field(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "field", + .lsb = 0, + .msb = 7, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *ac_access_register_aarsize_values[8] = { + [2] = "32bit", + [3] = "64bit", + [4] = "128bit" +}; +static const char *ac_access_register_aarpostincrement_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *ac_access_register_postexec_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *ac_access_register_transfer_values[2] = { + [0] = "disabled", + [1] = "enabled" +}; +static const char *ac_access_register_write_values[2] = { + [0] = "arg0", + [1] = "register" +}; +static riscv_debug_reg_field_list_t ac_access_register_get_cmdtype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmdtype", + .lsb = 0x18, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_aarsize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aarsize", + .lsb = 0x14, + .msb = 0x16, + .values = ac_access_register_aarsize_values + }, + .get_next = ac_access_register_get_cmdtype + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_aarpostincrement(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aarpostincrement", + .lsb = 0x13, + .msb = 0x13, + .values = ac_access_register_aarpostincrement_values + }, + .get_next = ac_access_register_get_aarsize + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_postexec(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "postexec", + .lsb = 0x12, + .msb = 0x12, + .values = ac_access_register_postexec_values + }, + .get_next = ac_access_register_get_aarpostincrement + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_transfer(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "transfer", + .lsb = 0x11, + .msb = 0x11, + .values = ac_access_register_transfer_values + }, + .get_next = ac_access_register_get_postexec + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_write(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "write", + .lsb = 0x10, + .msb = 0x10, + .values = ac_access_register_write_values + }, + .get_next = ac_access_register_get_transfer + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_register_get_regno(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "regno", + .lsb = 0, + .msb = 0xf, + .values = NULL + }, + .get_next = ac_access_register_get_write + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_quick_access_get_cmdtype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmdtype", + .lsb = 0x18, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static const char *ac_access_memory_aamvirtual_values[2] = { + [0] = "physical", + [1] = "virtual" +}; +static const char *ac_access_memory_aamsize_values[8] = { + [0] = "8bit", + [1] = "16bit", + [2] = "32bit", + [3] = "64bit", + [4] = "128bit" +}; +static const char *ac_access_memory_write_values[2] = { + [0] = "arg0", + [1] = "memory" +}; +static riscv_debug_reg_field_list_t ac_access_memory_get_cmdtype(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "cmdtype", + .lsb = 0x18, + .msb = 0x1f, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_aamvirtual(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aamvirtual", + .lsb = 0x17, + .msb = 0x17, + .values = ac_access_memory_aamvirtual_values + }, + .get_next = ac_access_memory_get_cmdtype + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_aamsize(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aamsize", + .lsb = 0x14, + .msb = 0x16, + .values = ac_access_memory_aamsize_values + }, + .get_next = ac_access_memory_get_aamvirtual + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_aampostincrement(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "aampostincrement", + .lsb = 0x13, + .msb = 0x13, + .values = NULL + }, + .get_next = ac_access_memory_get_aamsize + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_write(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "write", + .lsb = 0x10, + .msb = 0x10, + .values = ac_access_memory_write_values + }, + .get_next = ac_access_memory_get_aampostincrement + }; + return result; +} + +static riscv_debug_reg_field_list_t ac_access_memory_get_target_specific(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "target-specific", + .lsb = 0xe, + .msb = 0xf, + .values = NULL + }, + .get_next = ac_access_memory_get_write + }; + return result; +} + +static riscv_debug_reg_field_list_t virt_priv_get_v(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "v", + .lsb = 2, + .msb = 2, + .values = NULL + }, + .get_next = NULL + }; + return result; +} + +static riscv_debug_reg_field_list_t virt_priv_get_prv(riscv_debug_reg_ctx_t context) +{ + riscv_debug_reg_field_list_t result = { + .field = { + .name = "prv", + .lsb = 0, + .msb = 1, + .values = NULL + }, + .get_next = virt_priv_get_v + }; + return result; +} + +riscv_debug_reg_info_t get_riscv_debug_reg_info(enum riscv_debug_reg_ordinal reg_ordinal) +{ + static const riscv_debug_reg_info_t debug_reg_info[] = { + [DTM_IDCODE_ORDINAL] = { + .name = "idcode", + .get_fields_head = dtm_idcode_get_1 + }, + [DTM_DTMCS_ORDINAL] = { + .name = "dtmcs", + .get_fields_head = dtm_dtmcs_get_version + }, + [DTM_DMI_ORDINAL] = { + .name = "dmi", + .get_fields_head = dtm_dmi_get_op + }, + [DTM_BYPASS_ORDINAL] = { + .name = "bypass", + .get_fields_head = NULL + }, + [CSR_DCSR_ORDINAL] = { + .name = "dcsr", + .get_fields_head = csr_dcsr_get_prv + }, + [CSR_DPC_ORDINAL] = { + .name = "dpc", + .get_fields_head = csr_dpc_get_dpc + }, + [CSR_DSCRATCH0_ORDINAL] = { + .name = "dscratch0", + .get_fields_head = csr_dscratch0_get_dscratch0 + }, + [CSR_DSCRATCH1_ORDINAL] = { + .name = "dscratch1", + .get_fields_head = csr_dscratch1_get_dscratch1 + }, + [CSR_TSELECT_ORDINAL] = { + .name = "tselect", + .get_fields_head = csr_tselect_get_index + }, + [CSR_TDATA1_ORDINAL] = { + .name = "tdata1", + .get_fields_head = csr_tdata1_get_data + }, + [CSR_TDATA2_ORDINAL] = { + .name = "tdata2", + .get_fields_head = csr_tdata2_get_data + }, + [CSR_TDATA3_ORDINAL] = { + .name = "tdata3", + .get_fields_head = csr_tdata3_get_data + }, + [CSR_TINFO_ORDINAL] = { + .name = "tinfo", + .get_fields_head = csr_tinfo_get_info + }, + [CSR_TCONTROL_ORDINAL] = { + .name = "tcontrol", + .get_fields_head = csr_tcontrol_get_mte + }, + [CSR_SCONTEXT_ORDINAL] = { + .name = "scontext", + .get_fields_head = csr_scontext_get_data + }, + [CSR_MCONTEXT_ORDINAL] = { + .name = "mcontext", + .get_fields_head = csr_mcontext_get_hcontext + }, + [CSR_MCONTROL_ORDINAL] = { + .name = "mcontrol", + .get_fields_head = csr_mcontrol_get_load + }, + [CSR_MCONTROL6_ORDINAL] = { + .name = "mcontrol6", + .get_fields_head = csr_mcontrol6_get_load + }, + [CSR_ICOUNT_ORDINAL] = { + .name = "icount", + .get_fields_head = csr_icount_get_action + }, + [CSR_ITRIGGER_ORDINAL] = { + .name = "itrigger", + .get_fields_head = csr_itrigger_get_action + }, + [CSR_ETRIGGER_ORDINAL] = { + .name = "etrigger", + .get_fields_head = csr_etrigger_get_action + }, + [CSR_TMEXTTRIGGER_ORDINAL] = { + .name = "tmexttrigger", + .get_fields_head = csr_tmexttrigger_get_action + }, + [CSR_TEXTRA32_ORDINAL] = { + .name = "textra32", + .get_fields_head = csr_textra32_get_sselect + }, + [CSR_TEXTRA64_ORDINAL] = { + .name = "textra64", + .get_fields_head = csr_textra64_get_sselect + }, + [DM_DMSTATUS_ORDINAL] = { + .name = "dmstatus", + .get_fields_head = dm_dmstatus_get_version + }, + [DM_DMCONTROL_ORDINAL] = { + .name = "dmcontrol", + .get_fields_head = dm_dmcontrol_get_dmactive + }, + [DM_HARTINFO_ORDINAL] = { + .name = "hartinfo", + .get_fields_head = dm_hartinfo_get_dataaddr + }, + [DM_HAWINDOWSEL_ORDINAL] = { + .name = "hawindowsel", + .get_fields_head = dm_hawindowsel_get_hawindowsel + }, + [DM_HAWINDOW_ORDINAL] = { + .name = "hawindow", + .get_fields_head = dm_hawindow_get_maskdata + }, + [DM_ABSTRACTCS_ORDINAL] = { + .name = "abstractcs", + .get_fields_head = dm_abstractcs_get_datacount + }, + [DM_COMMAND_ORDINAL] = { + .name = "command", + .get_fields_head = dm_command_get_control + }, + [DM_ABSTRACTAUTO_ORDINAL] = { + .name = "abstractauto", + .get_fields_head = dm_abstractauto_get_autoexecdata + }, + [DM_CONFSTRPTR0_ORDINAL] = { + .name = "confstrptr0", + .get_fields_head = dm_confstrptr0_get_addr + }, + [DM_CONFSTRPTR1_ORDINAL] = { + .name = "confstrptr1", + .get_fields_head = dm_confstrptr1_get_addr + }, + [DM_CONFSTRPTR2_ORDINAL] = { + .name = "confstrptr2", + .get_fields_head = dm_confstrptr2_get_addr + }, + [DM_CONFSTRPTR3_ORDINAL] = { + .name = "confstrptr3", + .get_fields_head = dm_confstrptr3_get_addr + }, + [DM_NEXTDM_ORDINAL] = { + .name = "nextdm", + .get_fields_head = dm_nextdm_get_addr + }, + [DM_DATA0_ORDINAL] = { + .name = "data0", + .get_fields_head = dm_data0_get_data + }, + [DM_PROGBUF0_ORDINAL] = { + .name = "progbuf0", + .get_fields_head = dm_progbuf0_get_data + }, + [DM_AUTHDATA_ORDINAL] = { + .name = "authdata", + .get_fields_head = dm_authdata_get_data + }, + [DM_DMCS2_ORDINAL] = { + .name = "dmcs2", + .get_fields_head = dm_dmcs2_get_hgselect + }, + [DM_HALTSUM0_ORDINAL] = { + .name = "haltsum0", + .get_fields_head = dm_haltsum0_get_haltsum0 + }, + [DM_HALTSUM1_ORDINAL] = { + .name = "haltsum1", + .get_fields_head = dm_haltsum1_get_haltsum1 + }, + [DM_HALTSUM2_ORDINAL] = { + .name = "haltsum2", + .get_fields_head = dm_haltsum2_get_haltsum2 + }, + [DM_HALTSUM3_ORDINAL] = { + .name = "haltsum3", + .get_fields_head = dm_haltsum3_get_haltsum3 + }, + [DM_SBCS_ORDINAL] = { + .name = "sbcs", + .get_fields_head = dm_sbcs_get_sbaccess8 + }, + [DM_SBADDRESS0_ORDINAL] = { + .name = "sbaddress0", + .get_fields_head = dm_sbaddress0_get_address + }, + [DM_SBADDRESS1_ORDINAL] = { + .name = "sbaddress1", + .get_fields_head = dm_sbaddress1_get_address + }, + [DM_SBADDRESS2_ORDINAL] = { + .name = "sbaddress2", + .get_fields_head = dm_sbaddress2_get_address + }, + [DM_SBADDRESS3_ORDINAL] = { + .name = "sbaddress3", + .get_fields_head = dm_sbaddress3_get_address + }, + [DM_SBDATA0_ORDINAL] = { + .name = "sbdata0", + .get_fields_head = dm_sbdata0_get_data + }, + [DM_SBDATA1_ORDINAL] = { + .name = "sbdata1", + .get_fields_head = dm_sbdata1_get_data + }, + [DM_SBDATA2_ORDINAL] = { + .name = "sbdata2", + .get_fields_head = dm_sbdata2_get_data + }, + [DM_SBDATA3_ORDINAL] = { + .name = "sbdata3", + .get_fields_head = dm_sbdata3_get_data + }, + [SHORTNAME_ORDINAL] = { + .name = "shortname", + .get_fields_head = shortname_get_field + }, + [AC_ACCESS_REGISTER_ORDINAL] = { + .name = "access register", + .get_fields_head = ac_access_register_get_regno + }, + [AC_QUICK_ACCESS_ORDINAL] = { + .name = "quick access", + .get_fields_head = ac_quick_access_get_cmdtype + }, + [AC_ACCESS_MEMORY_ORDINAL] = { + .name = "access memory", + .get_fields_head = ac_access_memory_get_target_specific + }, + [VIRT_PRIV_ORDINAL] = { + .name = "priv", + .get_fields_head = virt_priv_get_prv + }, + }; + return debug_reg_info[reg_ordinal]; +} diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h index 8113d4766..0f6691b01 100644 --- a/src/target/riscv/debug_defines.h +++ b/src/target/riscv/debug_defines.h @@ -1,21 +1,21 @@ -/* - * This file is auto-generated by running 'make debug_defines.h' in - * https://github.com/riscv/riscv-debug-spec/ (d749752) - */ +/* SPDX-License-Identifier: BSD-2-Clause OR CC-BY-4.0 */ +/* This file was auto-generated by running 'make debug_defines' in https://github.com/riscv/riscv-debug-spec/ (22a7576) */ +#ifndef DEBUG_DEFINES_H +#define DEBUG_DEFINES_H #define DTM_IDCODE 0x01 /* * Identifies the release version of this part. */ -#define DTM_IDCODE_VERSION_OFFSET 0x1c -#define DTM_IDCODE_VERSION_LENGTH 4 -#define DTM_IDCODE_VERSION 0xf0000000U +#define DTM_IDCODE_VERSION_OFFSET 0x1cULL +#define DTM_IDCODE_VERSION_LENGTH 4ULL +#define DTM_IDCODE_VERSION 0xf0000000ULL /* * Identifies the designer's part number of this part. */ -#define DTM_IDCODE_PARTNUMBER_OFFSET 0xc -#define DTM_IDCODE_PARTNUMBER_LENGTH 0x10 -#define DTM_IDCODE_PARTNUMBER 0xffff000 +#define DTM_IDCODE_PARTNUMBER_OFFSET 0xcULL +#define DTM_IDCODE_PARTNUMBER_LENGTH 0x10ULL +#define DTM_IDCODE_PARTNUMBER 0xffff000ULL /* * Identifies the designer/manufacturer of this part. Bits 6:0 must be * bits 6:0 of the designer/manufacturer's Identification Code as @@ -23,13 +23,46 @@ * count of the number of continuation characters (0x7f) in that same * Identification Code. */ -#define DTM_IDCODE_MANUFID_OFFSET 1 -#define DTM_IDCODE_MANUFID_LENGTH 0xb -#define DTM_IDCODE_MANUFID 0xffe -#define DTM_IDCODE_1_OFFSET 0 -#define DTM_IDCODE_1_LENGTH 1 -#define DTM_IDCODE_1 1 +#define DTM_IDCODE_MANUFID_OFFSET 1ULL +#define DTM_IDCODE_MANUFID_LENGTH 0xbULL +#define DTM_IDCODE_MANUFID 0xffeULL +#define DTM_IDCODE_1_OFFSET 0ULL +#define DTM_IDCODE_1_LENGTH 1ULL +#define DTM_IDCODE_1 1ULL #define DTM_DTMCS 0x10 +/* + * This optional field may provide additional detail about an error + * that occurred when communicating with a DM. It is updated whenever + * {dmi-op} is updated by the hardware or when 1 is written to + * {dtmcs-dmireset}. + */ +#define DTM_DTMCS_ERRINFO_OFFSET 0x12ULL +#define DTM_DTMCS_ERRINFO_LENGTH 3ULL +#define DTM_DTMCS_ERRINFO 0x1c0000ULL +/* + * not implemented: This field is not implemented. + */ +#define DTM_DTMCS_ERRINFO_NOT_IMPLEMENTED 0 +/* + * dmi error: There was an error between the DTM and DMI. + */ +#define DTM_DTMCS_ERRINFO_DMI_ERROR 1 +/* + * communication error: There was an error between the DMI and a DMI subordinate. + */ +#define DTM_DTMCS_ERRINFO_COMMUNICATION_ERROR 2 +/* + * device error: The DMI subordinate reported an error. + */ +#define DTM_DTMCS_ERRINFO_DEVICE_ERROR 3 +/* + * unknown: There is no error to report, or no further information available + * about the error. This is the reset value if the field is implemented. + */ +#define DTM_DTMCS_ERRINFO_UNKNOWN 4 +/* + * Other values are reserved for future use by this specification. + */ /* * Writing 1 to this bit does a hard reset of the DTM, * causing the DTM to forget about any outstanding DMI transactions, and @@ -39,22 +72,22 @@ * complete (e.g. a reset condition caused an inflight DMI transaction to * be cancelled). */ -#define DTM_DTMCS_DMIHARDRESET_OFFSET 0x11 -#define DTM_DTMCS_DMIHARDRESET_LENGTH 1 -#define DTM_DTMCS_DMIHARDRESET 0x20000 +#define DTM_DTMCS_DTMHARDRESET_OFFSET 0x11ULL +#define DTM_DTMCS_DTMHARDRESET_LENGTH 1ULL +#define DTM_DTMCS_DTMHARDRESET 0x20000ULL /* - * Writing 1 to this bit clears the sticky error state, but does - * not affect outstanding DMI transactions. + * Writing 1 to this bit clears the sticky error state and resets + * {dtmcs-errinfo}, but does not affect outstanding DMI transactions. */ -#define DTM_DTMCS_DMIRESET_OFFSET 0x10 -#define DTM_DTMCS_DMIRESET_LENGTH 1 -#define DTM_DTMCS_DMIRESET 0x10000 +#define DTM_DTMCS_DMIRESET_OFFSET 0x10ULL +#define DTM_DTMCS_DMIRESET_LENGTH 1ULL +#define DTM_DTMCS_DMIRESET 0x10000ULL /* * This is a hint to the debugger of the minimum number of * cycles a debugger should spend in * Run-Test/Idle after every DMI scan to avoid a `busy' - * return code (\FdtmDtmcsDmistat of 3). A debugger must still - * check \FdtmDtmcsDmistat when necessary. + * return code ({dtmcs-dmistat} of 3). A debugger must still + * check {dtmcs-dmistat} when necessary. * * 0: It is not necessary to enter Run-Test/Idle at all. * @@ -64,24 +97,24 @@ * * And so on. */ -#define DTM_DTMCS_IDLE_OFFSET 0xc -#define DTM_DTMCS_IDLE_LENGTH 3 -#define DTM_DTMCS_IDLE 0x7000 +#define DTM_DTMCS_IDLE_OFFSET 0xcULL +#define DTM_DTMCS_IDLE_LENGTH 3ULL +#define DTM_DTMCS_IDLE 0x7000ULL /* - * Read-only alias of \FdtmDmiOp. + * Read-only alias of {dmi-op}. */ -#define DTM_DTMCS_DMISTAT_OFFSET 0xa -#define DTM_DTMCS_DMISTAT_LENGTH 2 -#define DTM_DTMCS_DMISTAT 0xc00 +#define DTM_DTMCS_DMISTAT_OFFSET 0xaULL +#define DTM_DTMCS_DMISTAT_LENGTH 2ULL +#define DTM_DTMCS_DMISTAT 0xc00ULL /* - * The size of \FdmSbaddressZeroAddress in \RdtmDmi. + * The size of {dmi-address} in {dtm-dmi}. */ -#define DTM_DTMCS_ABITS_OFFSET 4 -#define DTM_DTMCS_ABITS_LENGTH 6 -#define DTM_DTMCS_ABITS 0x3f0 -#define DTM_DTMCS_VERSION_OFFSET 0 -#define DTM_DTMCS_VERSION_LENGTH 4 -#define DTM_DTMCS_VERSION 0xf +#define DTM_DTMCS_ABITS_OFFSET 4ULL +#define DTM_DTMCS_ABITS_LENGTH 6ULL +#define DTM_DTMCS_ABITS 0x3f0ULL +#define DTM_DTMCS_VERSION_OFFSET 0ULL +#define DTM_DTMCS_VERSION_LENGTH 4ULL +#define DTM_DTMCS_VERSION 0xfULL /* * 0.11: Version described in spec version 0.11. */ @@ -98,38 +131,50 @@ /* * Address used for DMI access. In Update-DR this value is used * to access the DM over the DMI. + * {dmi-op} defines what this register contains after every possible + * operation. */ -#define DTM_DMI_ADDRESS_OFFSET 0x22 -#define DTM_DMI_ADDRESS_LENGTH(abits) abits -#define DTM_DMI_ADDRESS(abits) ((0x400000000ULL * (1ULL<> for priorities. */ -#define CSR_DCSR_CAUSE_OFFSET 6 -#define CSR_DCSR_CAUSE_LENGTH 3 -#define CSR_DCSR_CAUSE 0x1c0 +#define CSR_DCSR_CAUSE_OFFSET 6ULL +#define CSR_DCSR_CAUSE_LENGTH 3ULL +#define CSR_DCSR_CAUSE 0x1c0ULL /* - * ebreak: An {\tt ebreak} instruction was executed. + * ebreak: An `ebreak` instruction was executed. */ #define CSR_DCSR_CAUSE_EBREAK 1 /* @@ -326,15 +446,15 @@ */ #define CSR_DCSR_CAUSE_TRIGGER 2 /* - * haltreq: The debugger requested entry to Debug Mode using \FdmDmcontrolHaltreq. + * haltreq: The debugger requested entry to Debug Mode using {dmcontrol-haltreq}. */ #define CSR_DCSR_CAUSE_HALTREQ 3 /* - * step: The hart single stepped because \FcsrDcsrStep was set. + * step: The hart single stepped because {dcsr-step} was set. */ #define CSR_DCSR_CAUSE_STEP 4 /* - * resethaltreq: The hart halted directly out of reset due to \Fresethaltreq. It + * resethaltreq: The hart halted directly out of reset due to {resethaltreq} It * is also acceptable to report 3 when this happens. */ #define CSR_DCSR_CAUSE_RESETHALTREQ 5 @@ -344,28 +464,29 @@ */ #define CSR_DCSR_CAUSE_GROUP 6 /* - * Other values are reserved for future use. + * other: The hart halted for a reason other than the ones mentioned above. + * {dcsr-extcause} may contain a more specific reason. */ +#define CSR_DCSR_CAUSE_OTHER 7 /* * Extends the prv field with the virtualization mode the hart was operating - * in when Debug Mode was entered. The encoding is described in Table - * \ref{tab:privmode}. + * in when Debug Mode was entered. The encoding is described in <>. * A debugger can change this value to change the hart's virtualization mode * when exiting Debug Mode. * This bit is hardwired to 0 on harts that do not support virtualization mode. */ -#define CSR_DCSR_V_OFFSET 5 -#define CSR_DCSR_V_LENGTH 1 -#define CSR_DCSR_V 0x20 -#define CSR_DCSR_MPRVEN_OFFSET 4 -#define CSR_DCSR_MPRVEN_LENGTH 1 -#define CSR_DCSR_MPRVEN 0x10 +#define CSR_DCSR_V_OFFSET 5ULL +#define CSR_DCSR_V_LENGTH 1ULL +#define CSR_DCSR_V 0x20ULL +#define CSR_DCSR_MPRVEN_OFFSET 4ULL +#define CSR_DCSR_MPRVEN_LENGTH 1ULL +#define CSR_DCSR_MPRVEN 0x10ULL /* - * disabled: \FcsrMstatusMprv in \Rmstatus is ignored in Debug Mode. + * disabled: `mprv` in `mstatus` is ignored in Debug Mode. */ #define CSR_DCSR_MPRVEN_DISABLED 0 /* - * enabled: \FcsrMstatusMprv in \Rmstatus takes effect in Debug Mode. + * enabled: `mprv` in `mstatus` takes effect in Debug Mode. */ #define CSR_DCSR_MPRVEN_ENABLED 1 /* @@ -378,49 +499,54 @@ * reliable debugging may no longer be possible once this bit becomes set. * This is implementation-dependent. */ -#define CSR_DCSR_NMIP_OFFSET 3 -#define CSR_DCSR_NMIP_LENGTH 1 -#define CSR_DCSR_NMIP 8 +#define CSR_DCSR_NMIP_OFFSET 3ULL +#define CSR_DCSR_NMIP_LENGTH 1ULL +#define CSR_DCSR_NMIP 8ULL /* * When set and not in Debug Mode, the hart will only execute a single - * instruction and then enter Debug Mode. See Section~\ref{stepBit} + * instruction and then enter Debug Mode. See xref:stepbit[] * for details. * * The debugger must not change the value of this bit while the hart * is running. */ -#define CSR_DCSR_STEP_OFFSET 2 -#define CSR_DCSR_STEP_LENGTH 1 -#define CSR_DCSR_STEP 4 +#define CSR_DCSR_STEP_OFFSET 2ULL +#define CSR_DCSR_STEP_LENGTH 1ULL +#define CSR_DCSR_STEP 4ULL /* * Contains the privilege mode the hart was operating in when Debug - * Mode was entered. The encoding is described in Table - * \ref{tab:privmode}. A debugger can change this value to change + * Mode was entered. The encoding is described in <>. A debugger can change this value to change * the hart's privilege mode when exiting Debug Mode. * * Not all privilege modes are supported on all harts. If the * encoding written is not supported or the debugger is not allowed to * change to it, the hart may change to any supported privilege mode. */ -#define CSR_DCSR_PRV_OFFSET 0 -#define CSR_DCSR_PRV_LENGTH 2 -#define CSR_DCSR_PRV 3 +#define CSR_DCSR_PRV_OFFSET 0ULL +#define CSR_DCSR_PRV_LENGTH 2ULL +#define CSR_DCSR_PRV 3ULL #define CSR_DPC 0x7b1 -#define CSR_DPC_DPC_OFFSET 0 -#define CSR_DPC_DPC_LENGTH(DXLEN) DXLEN -#define CSR_DPC_DPC(DXLEN) ((1ULL< 0, otherwise it's undefined on what conditions the * trigger will match. */ #define CSR_MCONTROL_MATCH_NAPOT 1 /* * ge: Matches when any compare value is greater than (unsigned) or - * equal to \RcsrTdataTwo. + * equal to {csr-tdata2}. */ #define CSR_MCONTROL_MATCH_GE 2 /* * lt: Matches when any compare value is less than (unsigned) - * \RcsrTdataTwo. + * {csr-tdata2}. */ #define CSR_MCONTROL_MATCH_LT 3 /* - * mask low: Matches when $\frac{|XLEN|}{2}-1$:$0$ of any compare value - * equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after - * $\frac{|XLEN|}{2}-1$:$0$ of the compare value is ANDed with - * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo. + * mask low: Matches when latexmath:[$\frac{XLEN}{2}-{1:0}] of any compare value + * equals latexmath:[$\frac{XLEN}{2}-{1:0}] of {csr-tdata2} after + * latexmath:[$\frac{XLEN}{2}-{1:0}] of the compare value is ANDed with + * `XLEN-1`:latexmath:[$\frac{XLEN}{2}$] of {csr-tdata2}. */ #define CSR_MCONTROL_MATCH_MASK_LOW 4 /* - * mask high: Matches when $|XLEN|-1$:$\frac{|XLEN|}{2}$ of any compare - * value equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after - * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of the compare value is ANDed with - * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo. + * mask high: Matches when `XLEN-1`:latexmath:[$\frac{XLEN}{2}$] of any compare + * value equals latexmath:[$\frac{XLEN}{2}-{1:0}] of {csr-tdata2} after + * `XLEN-1`:latexmath:[$\frac{XLEN}{2}$] of the compare value is ANDed with + * `XLEN-1`:latexmath:[$\frac{XLEN}{2}$] of {csr-tdata2}. */ #define CSR_MCONTROL_MATCH_MASK_HIGH 5 /* - * not equal: Matches when \FcsrMcontrolMatch$=0$ would not match. + * not equal: Matches when {mcontrol-match}=0 would not match. */ #define CSR_MCONTROL_MATCH_NOT_EQUAL 8 /* - * not napot: Matches when \FcsrMcontrolMatch$=1$ would not match. + * not napot: Matches when {mcontrol-match}=1 would not match. */ #define CSR_MCONTROL_MATCH_NOT_NAPOT 9 /* - * not mask low: Matches when \FcsrMcontrolMatch$=4$ would not match. + * not mask low: Matches when {mcontrol-match}=4 would not match. */ #define CSR_MCONTROL_MATCH_NOT_MASK_LOW 12 /* - * not mask high: Matches when \FcsrMcontrolMatch$=5$ would not match. + * not mask high: Matches when {mcontrol-match}=5 would not match. */ #define CSR_MCONTROL_MATCH_NOT_MASK_HIGH 13 /* * Other values are reserved for future use. * * All comparisons only look at the lower XLEN (in the current mode) - * bits of the compare values and of \RcsrTdataTwo. - * When \FcsrMcontrolSelect=1 and access size is N, this is further + * bits of the compare values and of {csr-tdata2}. + * When {mcontrol-select}=1 and access size is N, this is further * reduced, and comparisons only look at the lower N bits of the - * compare values and of \RcsrTdataTwo. + * compare values and of {csr-tdata2}. */ /* * When set, enable this trigger in M-mode. */ -#define CSR_MCONTROL_M_OFFSET 6 -#define CSR_MCONTROL_M_LENGTH 1 -#define CSR_MCONTROL_M 0x40 +#define CSR_MCONTROL_M_OFFSET 6ULL +#define CSR_MCONTROL_M_LENGTH 1ULL +#define CSR_MCONTROL_M 0x40ULL /* * When set, enable this trigger in S/HS-mode. * This bit is hard-wired to 0 if the hart does not support * S-mode. */ -#define CSR_MCONTROL_S_OFFSET 4 -#define CSR_MCONTROL_S_LENGTH 1 -#define CSR_MCONTROL_S 0x10 +#define CSR_MCONTROL_S_OFFSET 4ULL +#define CSR_MCONTROL_S_LENGTH 1ULL +#define CSR_MCONTROL_S 0x10ULL /* * When set, enable this trigger in U-mode. * This bit is hard-wired to 0 if the hart does not support * U-mode. */ -#define CSR_MCONTROL_U_OFFSET 3 -#define CSR_MCONTROL_U_LENGTH 1 -#define CSR_MCONTROL_U 8 +#define CSR_MCONTROL_U_OFFSET 3ULL +#define CSR_MCONTROL_U_LENGTH 1ULL +#define CSR_MCONTROL_U 8ULL /* * When set, the trigger fires on the virtual address or opcode of an * instruction that is executed. */ -#define CSR_MCONTROL_EXECUTE_OFFSET 2 -#define CSR_MCONTROL_EXECUTE_LENGTH 1 -#define CSR_MCONTROL_EXECUTE 4 +#define CSR_MCONTROL_EXECUTE_OFFSET 2ULL +#define CSR_MCONTROL_EXECUTE_LENGTH 1ULL +#define CSR_MCONTROL_EXECUTE 4ULL /* * When set, the trigger fires on the virtual address or data of any * store. */ -#define CSR_MCONTROL_STORE_OFFSET 1 -#define CSR_MCONTROL_STORE_LENGTH 1 -#define CSR_MCONTROL_STORE 2 +#define CSR_MCONTROL_STORE_OFFSET 1ULL +#define CSR_MCONTROL_STORE_LENGTH 1ULL +#define CSR_MCONTROL_STORE 2ULL /* * When set, the trigger fires on the virtual address or data of any * load. */ -#define CSR_MCONTROL_LOAD_OFFSET 0 -#define CSR_MCONTROL_LOAD_LENGTH 1 -#define CSR_MCONTROL_LOAD 1 +#define CSR_MCONTROL_LOAD_OFFSET 0ULL +#define CSR_MCONTROL_LOAD_LENGTH 1ULL +#define CSR_MCONTROL_LOAD 1ULL #define CSR_MCONTROL6 0x7a1 -#define CSR_MCONTROL6_TYPE_OFFSET(XLEN) (XLEN + -4) -#define CSR_MCONTROL6_TYPE_LENGTH 4 -#define CSR_MCONTROL6_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_MCONTROL6_DMODE_OFFSET(XLEN) (XLEN + -5) -#define CSR_MCONTROL6_DMODE_LENGTH 1 -#define CSR_MCONTROL6_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_MCONTROL6_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL) +#define CSR_MCONTROL6_TYPE_LENGTH 4ULL +#define CSR_MCONTROL6_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL))) +#define CSR_MCONTROL6_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL) +#define CSR_MCONTROL6_DMODE_LENGTH 1ULL +#define CSR_MCONTROL6_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL)) +/* + * If implemented, the TM updates this field every time the trigger + * fires. + */ +#define CSR_MCONTROL6_UNCERTAIN_OFFSET 0x1aULL +#define CSR_MCONTROL6_UNCERTAIN_LENGTH 1ULL +#define CSR_MCONTROL6_UNCERTAIN 0x4000000ULL +/* + * certain: The trigger that fired satisfied the configured conditions, or + * this bit is not implemented. + */ +#define CSR_MCONTROL6_UNCERTAIN_CERTAIN 0 +/* + * uncertain: The trigger that fired might not have perfectly satisfied the + * configured conditions. Due to the implementation the hardware + * cannot be certain. + */ +#define CSR_MCONTROL6_UNCERTAIN_UNCERTAIN 1 +#define CSR_MCONTROL6_HIT1_OFFSET 0x19ULL +#define CSR_MCONTROL6_HIT1_LENGTH 1ULL +#define CSR_MCONTROL6_HIT1 0x2000000ULL /* * When set, enable this trigger in VS-mode. * This bit is hard-wired to 0 if the hart does not support * virtualization mode. */ -#define CSR_MCONTROL6_VS_OFFSET 0x18 -#define CSR_MCONTROL6_VS_LENGTH 1 -#define CSR_MCONTROL6_VS 0x1000000 +#define CSR_MCONTROL6_VS_OFFSET 0x18ULL +#define CSR_MCONTROL6_VS_LENGTH 1ULL +#define CSR_MCONTROL6_VS 0x1000000ULL /* * When set, enable this trigger in VU-mode. * This bit is hard-wired to 0 if the hart does not support * virtualization mode. */ -#define CSR_MCONTROL6_VU_OFFSET 0x17 -#define CSR_MCONTROL6_VU_LENGTH 1 -#define CSR_MCONTROL6_VU 0x800000 +#define CSR_MCONTROL6_VU_OFFSET 0x17ULL +#define CSR_MCONTROL6_VU_LENGTH 1ULL +#define CSR_MCONTROL6_VU 0x800000ULL /* - * If this bit is implemented then it must become set when this - * trigger fires and may become set when this trigger matches. - * The trigger's user can set or clear it at any - * time. It is used to determine which - * trigger(s) matched. If the bit is not implemented, it is always 0 - * and writing it has no effect. + * If they are implemented, {mcontrol6-hit1} (MSB) and + * {mcontrol6-hit0} (LSB) combine into a single 2-bit field. + * The TM updates this field when the trigger fires. After the debugger + * has seen the update, it will normally write 0 to this field to so it + * can see future changes. + * + * If either of the bits is not implemented, the unimplemented bits + * will be read-only 0. */ -#define CSR_MCONTROL6_HIT_OFFSET 0x16 -#define CSR_MCONTROL6_HIT_LENGTH 1 -#define CSR_MCONTROL6_HIT 0x400000 +#define CSR_MCONTROL6_HIT0_OFFSET 0x16ULL +#define CSR_MCONTROL6_HIT0_LENGTH 1ULL +#define CSR_MCONTROL6_HIT0 0x400000ULL +/* + * false: The trigger did not fire. + */ +#define CSR_MCONTROL6_HIT0_FALSE 0 +/* + * before: The trigger fired before the instruction that matched it was + * retired, but after all preceding instructions are retired. This + * explicitly allows for instructions to be partially executed, as + * described in xref:multistate[]. + * + * `xepc` or {csr-dpc} (depending on {mcontrol6-action}) must be set + * to the virtual address of the instruction that matched. + */ +#define CSR_MCONTROL6_HIT0_BEFORE 1 +/* + * after: The trigger fired after the instruction that triggered and at least + * one additional instruction were retired. + * `xepc` or {csr-dpc} (depending on {mcontrol6-action}) must be set + * to the virtual address of the next instruction that must be executed + * to preserve the program flow. + */ +#define CSR_MCONTROL6_HIT0_AFTER 2 +/* + * immediately after: The trigger fired just after the instruction that triggered it was + * retired, but before any subsequent instructions were executed. + * `xepc` or {csr-dpc} (depending on {mcontrol6-action}) must be set + * to the virtual address of the next instruction that must be executed + * to preserve the program flow. + * + * If the instruction performed multiple memory accesses, all of them + * have been completed. + */ +#define CSR_MCONTROL6_HIT0_IMMEDIATELY_AFTER 3 /* * This bit determines the contents of the XLEN-bit compare values. */ -#define CSR_MCONTROL6_SELECT_OFFSET 0x15 -#define CSR_MCONTROL6_SELECT_LENGTH 1 -#define CSR_MCONTROL6_SELECT 0x200000 +#define CSR_MCONTROL6_SELECT_OFFSET 0x15ULL +#define CSR_MCONTROL6_SELECT_LENGTH 1ULL +#define CSR_MCONTROL6_SELECT 0x200000ULL /* * address: There is at least one compare value and it contains the lowest * virtual address of the access. @@ -1000,58 +1209,13 @@ * Any bits beyond the size of the data access will contain 0. */ #define CSR_MCONTROL6_SELECT_DATA 1 -#define CSR_MCONTROL6_TIMING_OFFSET 0x14 -#define CSR_MCONTROL6_TIMING_LENGTH 1 -#define CSR_MCONTROL6_TIMING 0x100000 -/* - * before: The action for this trigger will be taken just before the - * instruction that triggered it is committed, but after all preceding - * instructions are committed. \Rxepc or \RcsrDpc (depending - * on \FcsrMcontrolSixAction) must be set to the virtual address of the - * instruction that matched. - * - * If this is combined with \FcsrMcontrolSixLoad and - * \FcsrMcontrolSixSelect=1 then a memory access will be - * performed (including any side effects of performing such an access) even - * though the load will not update its destination register. Debuggers - * should consider this when setting such breakpoints on, for example, - * memory-mapped I/O addresses. - */ -#define CSR_MCONTROL6_TIMING_BEFORE 0 -/* - * after: The action for this trigger will be taken after the instruction - * that triggered it is committed. It should be taken before the next - * instruction is committed, but it is better to implement triggers imprecisely - * than to not implement them at all. \Rxepc or - * \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set to - * the virtual address of the next instruction that must be executed to - * preserve the program flow. - */ -#define CSR_MCONTROL6_TIMING_AFTER 1 -/* - * Most hardware will only implement one timing or the other, possibly - * dependent on \FcsrMcontrolSixSelect, \FcsrMcontrolSixExecute, - * \FcsrMcontrolSixLoad, and \FcsrMcontrolSixStore. This bit - * primarily exists for the hardware to communicate to the debugger - * what will happen. Hardware may implement the bit fully writable, in - * which case the debugger has a little more control. - * - * Data load triggers with \FcsrMcontrolSixTiming of 0 will result in the same load - * happening again when the debugger lets the hart run. For data load - * triggers, debuggers must first attempt to set the breakpoint with - * \FcsrMcontrolSixTiming of 1. - * - * If a trigger with \FcsrMcontrolSixTiming of 0 matches, it is - * implementation-dependent whether that prevents a trigger with - * \FcsrMcontrolSixTiming of 1 matching as well. - */ -#define CSR_MCONTROL6_SIZE_OFFSET 0x10 -#define CSR_MCONTROL6_SIZE_LENGTH 4 -#define CSR_MCONTROL6_SIZE 0xf0000 +#define CSR_MCONTROL6_SIZE_OFFSET 0x10ULL +#define CSR_MCONTROL6_SIZE_LENGTH 3ULL +#define CSR_MCONTROL6_SIZE 0x70000ULL /* * any: The trigger will attempt to match against an access of any size. - * The behavior is only well-defined if $|select|=0$, or if the access - * size is XLEN. + * The behavior is only well-defined if {mcontrol6-select}=0, or if the + * access size is XLEN. */ #define CSR_MCONTROL6_SIZE_ANY 0 /* @@ -1077,48 +1241,36 @@ * execution of 64-bit instructions. */ #define CSR_MCONTROL6_SIZE_64BIT 5 -/* - * 80bit: The trigger will only match against execution of 80-bit instructions. - */ -#define CSR_MCONTROL6_SIZE_80BIT 6 -/* - * 96bit: The trigger will only match against execution of 96-bit instructions. - */ -#define CSR_MCONTROL6_SIZE_96BIT 7 -/* - * 112bit: The trigger will only match against execution of 112-bit instructions. - */ -#define CSR_MCONTROL6_SIZE_112BIT 8 /* * 128bit: The trigger will only match against 128-bit memory accesses or * execution of 128-bit instructions. */ -#define CSR_MCONTROL6_SIZE_128BIT 9 +#define CSR_MCONTROL6_SIZE_128BIT 6 /* * An implementation must support the value of 0, but all other values * are optional. When an implementation supports address triggers - * (\FcsrMcontrolSixSelect=0), it is recommended that those triggers + * ({mcontrol6-select}=0), it is recommended that those triggers * support every access size that the hart supports, as well as for * every instruction size that the hart supports. * * Implementations such as RV32D or RV64V are able to perform loads * and stores that are wider than XLEN. Custom extensions may also * support instructions that are wider than XLEN. Because - * \RcsrTdataTwo is of size XLEN, there is a known limitation that - * data value triggers (\FcsrMcontrolSixSelect=1) can only be supported + * {csr-tdata2} is of size XLEN, there is a known limitation that + * data value triggers ({mcontrol6-select}=1) can only be supported * for access sizes up to XLEN bits. When an implementation supports - * data value triggers (\FcsrMcontrolSixSelect=1), it is recommended + * data value triggers ({mcontrol6-select}=1), it is recommended * that those triggers support every access size up to XLEN that the * hart supports, as well as for every instruction length up to XLEN * that the hart supports. */ /* * The action to take when the trigger fires. The values are explained - * in Table~\ref{tab:action}. + * in xref:tab:action[]. */ -#define CSR_MCONTROL6_ACTION_OFFSET 0xc -#define CSR_MCONTROL6_ACTION_LENGTH 4 -#define CSR_MCONTROL6_ACTION 0xf000 +#define CSR_MCONTROL6_ACTION_OFFSET 0xcULL +#define CSR_MCONTROL6_ACTION_LENGTH 4ULL +#define CSR_MCONTROL6_ACTION 0xf000ULL /* * breakpoint: */ @@ -1147,9 +1299,9 @@ * external1: */ #define CSR_MCONTROL6_ACTION_EXTERNAL1 9 -#define CSR_MCONTROL6_CHAIN_OFFSET 0xb -#define CSR_MCONTROL6_CHAIN_LENGTH 1 -#define CSR_MCONTROL6_CHAIN 0x800 +#define CSR_MCONTROL6_CHAIN_OFFSET 0xbULL +#define CSR_MCONTROL6_CHAIN_LENGTH 1ULL +#define CSR_MCONTROL6_CHAIN 0x800ULL /* * disabled: When this trigger matches, the configured action is taken. */ @@ -1160,10 +1312,10 @@ */ #define CSR_MCONTROL6_CHAIN_ENABLED 1 /* - * A trigger chain starts on the first trigger with $|chain|=1$ after - * a trigger with $|chain|=0$, or simply on the first trigger if that - * has $|chain|=1$. It ends on the first trigger after that which has - * $|chain|=0$. This final trigger is part of the chain. The action + * A trigger chain starts on the first trigger with `chain`=1 after + * a trigger with `chain`=0, or simply on the first trigger if that + * has `chain`=1. It ends on the first trigger after that which has + * `chain`=0. This final trigger is part of the chain. The action * on all but the final trigger is ignored. The action on that final * trigger will be taken if and only if all the triggers in the chain * match at the same time. @@ -1171,152 +1323,166 @@ * Debuggers should not terminate a chain with a trigger with a * different type. It is undefined when exactly such a chain fires. * - * Because \FcsrMcontrolSixChain affects the next trigger, hardware must zero it in - * writes to \RcsrMcontrolSix that set \FcsrTdataOneDmode to 0 if the next trigger has - * \FcsrTdataOneDmode of 1. - * In addition hardware should ignore writes to \RcsrMcontrolSix that set - * \FcsrTdataOneDmode to 1 if the previous trigger has both \FcsrTdataOneDmode of 0 and - * \FcsrMcontrolSixChain of 1. Debuggers must avoid the latter case by checking - * \FcsrMcontrolSixChain on the previous trigger if they're writing \RcsrMcontrolSix. + * Because {mcontrol6-chain} affects the next trigger, hardware must zero it in + * writes to {csr-mcontrol6} that set {tdata1-dmode} to 0 if the next trigger has + * {tdata1-dmode} of 1. + * In addition hardware should ignore writes to {csr-mcontrol6} that set + * {tdata1-dmode} to 1 if the previous trigger has both {tdata1-dmode} of 0 and + * {mcontrol6-chain} of 1. Debuggers must avoid the latter case by checking + * {mcontrol6-chain} on the previous trigger if they're writing {csr-mcontrol6}. * * Implementations that wish to limit the maximum length of a trigger * chain (eg. to meet timing requirements) may do so by zeroing - * \FcsrMcontrolSixChain in writes to \RcsrMcontrolSix that would make the chain too long. + * {mcontrol6-chain} in writes to {csr-mcontrol6} that would make the chain too long. */ -#define CSR_MCONTROL6_MATCH_OFFSET 7 -#define CSR_MCONTROL6_MATCH_LENGTH 4 -#define CSR_MCONTROL6_MATCH 0x780 +#define CSR_MCONTROL6_MATCH_OFFSET 7ULL +#define CSR_MCONTROL6_MATCH_LENGTH 4ULL +#define CSR_MCONTROL6_MATCH 0x780ULL /* - * equal: Matches when any compare value equals \RcsrTdataTwo. + * equal: Matches when any compare value equals {csr-tdata2}. */ #define CSR_MCONTROL6_MATCH_EQUAL 0 /* - * napot: Matches when the top $M$ bits of any compare value match the top - * $M$ bits of \RcsrTdataTwo. - * $M$ is $|XLEN|-1$ minus the index of the least-significant bit - * containing 0 in \RcsrTdataTwo. - * \RcsrTdataTwo is WARL and if bits $|maskmax6|-1$:0 are written with all - * ones then bit $|maskmax6|-1$ will be set to 0 while the values of bits $|maskmax6|-2$:0 - * are \unspecified. - * Legal values for \RcsrTdataTwo require $M + |maskmax6| \geq |XLEN|$ and $M\gt0$. + * napot: Matches when the top `M` bits of any compare value match the top + * `M` bits of {csr-tdata2}. + * `M` is `XLEN-1` minus the index of the least-significant bit + * containing 0 in {csr-tdata2}. + * {csr-tdata2} is *WARL* and if bits `maskmax6-1:0` are written with all + * ones then bit `maskmax6-1` will be set to 0 while the values of bits `maskmax6-2:0` + * are UNSPECIFIED. + * Legal values for {csr-tdata2} require M + `maskmax6` ≥ `XLEN` and `M` > 0. * See above for how to determine maskmax6. */ #define CSR_MCONTROL6_MATCH_NAPOT 1 /* * ge: Matches when any compare value is greater than (unsigned) or - * equal to \RcsrTdataTwo. + * equal to {csr-tdata2}. */ #define CSR_MCONTROL6_MATCH_GE 2 /* * lt: Matches when any compare value is less than (unsigned) - * \RcsrTdataTwo. + * {csr-tdata2}. */ #define CSR_MCONTROL6_MATCH_LT 3 /* - * mask low: Matches when $\frac{|XLEN|}{2}-1$:$0$ of any compare value - * equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after - * $\frac{|XLEN|}{2}-1$:$0$ of the compare value is ANDed with - * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo. + * mask low: Matches when latexmath:[$\frac{XLEN}{2}-{1:0}] of any compare value + * equals latexmath:[$\frac{XLEN}{2}-{1:0}] of {csr-tdata2} after + * latexmath:[$\frac{XLEN}{2}-{1:0}] of the compare value is ANDed with + * `XLEN-1`:latexmath:[$\frac{XLEN}{2}$] of {csr-tdata2}. */ #define CSR_MCONTROL6_MATCH_MASK_LOW 4 /* - * mask high: Matches when $|XLEN|-1$:$\frac{|XLEN|}{2}$ of any compare - * value equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after - * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of the compare value is ANDed with - * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo. + * mask high: Matches when `XLEN-1`:latexmath:[$\frac{XLEN}{2}$] of any compare + * value equals latexmath:[$\frac{XLEN}{2}-{1:0}] of {csr-tdata2} after + * `XLEN-1`:latexmath:[$\frac{XLEN}{2}$] of the compare value is ANDed with + * `XLEN-1`:latexmath:[$\frac{XLEN}{2}$] of {csr-tdata2}. */ #define CSR_MCONTROL6_MATCH_MASK_HIGH 5 /* - * not equal: Matches when \FcsrMcontrolSixMatch$=0$ would not match. + * not equal: Matches when {mcontrol6-match} `=0` would not match. */ #define CSR_MCONTROL6_MATCH_NOT_EQUAL 8 /* - * not napot: Matches when \FcsrMcontrolSixMatch$=1$ would not match. + * not napot: Matches when {mcontrol6-match} `=1` would not match. */ #define CSR_MCONTROL6_MATCH_NOT_NAPOT 9 /* - * not mask low: Matches when \FcsrMcontrolSixMatch$=4$ would not match. + * not mask low: Matches when {mcontrol6-match} `=4` would not match. */ #define CSR_MCONTROL6_MATCH_NOT_MASK_LOW 12 /* - * not mask high: Matches when \FcsrMcontrolSixMatch$=5$ would not match. + * not mask high: Matches when {mcontrol6-match} `=5` would not match. */ #define CSR_MCONTROL6_MATCH_NOT_MASK_HIGH 13 /* * Other values are reserved for future use. * * All comparisons only look at the lower XLEN (in the current mode) - * bits of the compare values and of \RcsrTdataTwo. - * When \FcsrMcontrolSelect=1 and access size is N, this is further + * bits of the compare values and of {csr-tdata2}. + * When {mcontrol-select}=1 and access size is N, this is further * reduced, and comparisons only look at the lower N bits of the - * compare values and of \RcsrTdataTwo. + * compare values and of {csr-tdata2}. */ /* * When set, enable this trigger in M-mode. */ -#define CSR_MCONTROL6_M_OFFSET 6 -#define CSR_MCONTROL6_M_LENGTH 1 -#define CSR_MCONTROL6_M 0x40 +#define CSR_MCONTROL6_M_OFFSET 6ULL +#define CSR_MCONTROL6_M_LENGTH 1ULL +#define CSR_MCONTROL6_M 0x40ULL +#define CSR_MCONTROL6_UNCERTAINEN_OFFSET 5ULL +#define CSR_MCONTROL6_UNCERTAINEN_LENGTH 1ULL +#define CSR_MCONTROL6_UNCERTAINEN 0x20ULL +/* + * disabled: This trigger will only match if the hardware can perfectly + * evaluate it. + */ +#define CSR_MCONTROL6_UNCERTAINEN_DISABLED 0 +/* + * enabled: This trigger will match if it's possible that it would match if + * the Trigger Module had perfect information about the operations + * being performed. + */ +#define CSR_MCONTROL6_UNCERTAINEN_ENABLED 1 /* * When set, enable this trigger in S/HS-mode. * This bit is hard-wired to 0 if the hart does not support * S-mode. */ -#define CSR_MCONTROL6_S_OFFSET 4 -#define CSR_MCONTROL6_S_LENGTH 1 -#define CSR_MCONTROL6_S 0x10 +#define CSR_MCONTROL6_S_OFFSET 4ULL +#define CSR_MCONTROL6_S_LENGTH 1ULL +#define CSR_MCONTROL6_S 0x10ULL /* * When set, enable this trigger in U-mode. * This bit is hard-wired to 0 if the hart does not support * U-mode. */ -#define CSR_MCONTROL6_U_OFFSET 3 -#define CSR_MCONTROL6_U_LENGTH 1 -#define CSR_MCONTROL6_U 8 +#define CSR_MCONTROL6_U_OFFSET 3ULL +#define CSR_MCONTROL6_U_LENGTH 1ULL +#define CSR_MCONTROL6_U 8ULL /* * When set, the trigger fires on the virtual address or opcode of an * instruction that is executed. */ -#define CSR_MCONTROL6_EXECUTE_OFFSET 2 -#define CSR_MCONTROL6_EXECUTE_LENGTH 1 -#define CSR_MCONTROL6_EXECUTE 4 +#define CSR_MCONTROL6_EXECUTE_OFFSET 2ULL +#define CSR_MCONTROL6_EXECUTE_LENGTH 1ULL +#define CSR_MCONTROL6_EXECUTE 4ULL /* * When set, the trigger fires on the virtual address or data of any * store. */ -#define CSR_MCONTROL6_STORE_OFFSET 1 -#define CSR_MCONTROL6_STORE_LENGTH 1 -#define CSR_MCONTROL6_STORE 2 +#define CSR_MCONTROL6_STORE_OFFSET 1ULL +#define CSR_MCONTROL6_STORE_LENGTH 1ULL +#define CSR_MCONTROL6_STORE 2ULL /* * When set, the trigger fires on the virtual address or data of any * load. */ -#define CSR_MCONTROL6_LOAD_OFFSET 0 -#define CSR_MCONTROL6_LOAD_LENGTH 1 -#define CSR_MCONTROL6_LOAD 1 +#define CSR_MCONTROL6_LOAD_OFFSET 0ULL +#define CSR_MCONTROL6_LOAD_LENGTH 1ULL +#define CSR_MCONTROL6_LOAD 1ULL #define CSR_ICOUNT 0x7a1 -#define CSR_ICOUNT_TYPE_OFFSET(XLEN) (XLEN + -4) -#define CSR_ICOUNT_TYPE_LENGTH 4 -#define CSR_ICOUNT_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_ICOUNT_DMODE_OFFSET(XLEN) (XLEN + -5) -#define CSR_ICOUNT_DMODE_LENGTH 1 -#define CSR_ICOUNT_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_ICOUNT_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL) +#define CSR_ICOUNT_TYPE_LENGTH 4ULL +#define CSR_ICOUNT_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL))) +#define CSR_ICOUNT_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL) +#define CSR_ICOUNT_DMODE_LENGTH 1ULL +#define CSR_ICOUNT_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL)) /* * When set, enable this trigger in VS-mode. * This bit is hard-wired to 0 if the hart does not support * virtualization mode. */ -#define CSR_ICOUNT_VS_OFFSET 0x1a -#define CSR_ICOUNT_VS_LENGTH 1 -#define CSR_ICOUNT_VS 0x4000000 +#define CSR_ICOUNT_VS_OFFSET 0x1aULL +#define CSR_ICOUNT_VS_LENGTH 1ULL +#define CSR_ICOUNT_VS 0x4000000ULL /* * When set, enable this trigger in VU-mode. * This bit is hard-wired to 0 if the hart does not support * virtualization mode. */ -#define CSR_ICOUNT_VU_OFFSET 0x19 -#define CSR_ICOUNT_VU_LENGTH 1 -#define CSR_ICOUNT_VU 0x2000000 +#define CSR_ICOUNT_VU_OFFSET 0x19ULL +#define CSR_ICOUNT_VU_LENGTH 1ULL +#define CSR_ICOUNT_VU 0x2000000ULL /* * If this bit is implemented, the hardware sets it when this * trigger fires. The trigger's user can set or clear it at any @@ -1324,53 +1490,53 @@ * trigger(s) fires. If the bit is not implemented, it is always 0 * and writing it has no effect. */ -#define CSR_ICOUNT_HIT_OFFSET 0x18 -#define CSR_ICOUNT_HIT_LENGTH 1 -#define CSR_ICOUNT_HIT 0x1000000 +#define CSR_ICOUNT_HIT_OFFSET 0x18ULL +#define CSR_ICOUNT_HIT_LENGTH 1ULL +#define CSR_ICOUNT_HIT 0x1000000ULL /* - * The trigger will generally fire after \FcsrIcountCount instructions + * The trigger will generally fire after {icount-count} instructions * in enabled modes have been executed. See above for the precise behavior. */ -#define CSR_ICOUNT_COUNT_OFFSET 0xa -#define CSR_ICOUNT_COUNT_LENGTH 0xe -#define CSR_ICOUNT_COUNT 0xfffc00 +#define CSR_ICOUNT_COUNT_OFFSET 0xaULL +#define CSR_ICOUNT_COUNT_LENGTH 0xeULL +#define CSR_ICOUNT_COUNT 0xfffc00ULL /* * When set, enable this trigger in M-mode. */ -#define CSR_ICOUNT_M_OFFSET 9 -#define CSR_ICOUNT_M_LENGTH 1 -#define CSR_ICOUNT_M 0x200 +#define CSR_ICOUNT_M_OFFSET 9ULL +#define CSR_ICOUNT_M_LENGTH 1ULL +#define CSR_ICOUNT_M 0x200ULL /* - * This bit becomes set when \FcsrIcountCount is decremented from 1 + * This bit becomes set when {icount-count} is decremented from 1 * to 0. It is cleared when the trigger fires, which will happen just * before executing the next instruction in one of the enabled modes. */ -#define CSR_ICOUNT_PENDING_OFFSET 8 -#define CSR_ICOUNT_PENDING_LENGTH 1 -#define CSR_ICOUNT_PENDING 0x100 +#define CSR_ICOUNT_PENDING_OFFSET 8ULL +#define CSR_ICOUNT_PENDING_LENGTH 1ULL +#define CSR_ICOUNT_PENDING 0x100ULL /* * When set, enable this trigger in S/HS-mode. * This bit is hard-wired to 0 if the hart does not support * S-mode. */ -#define CSR_ICOUNT_S_OFFSET 7 -#define CSR_ICOUNT_S_LENGTH 1 -#define CSR_ICOUNT_S 0x80 +#define CSR_ICOUNT_S_OFFSET 7ULL +#define CSR_ICOUNT_S_LENGTH 1ULL +#define CSR_ICOUNT_S 0x80ULL /* * When set, enable this trigger in U-mode. * This bit is hard-wired to 0 if the hart does not support * U-mode. */ -#define CSR_ICOUNT_U_OFFSET 6 -#define CSR_ICOUNT_U_LENGTH 1 -#define CSR_ICOUNT_U 0x40 +#define CSR_ICOUNT_U_OFFSET 6ULL +#define CSR_ICOUNT_U_LENGTH 1ULL +#define CSR_ICOUNT_U 0x40ULL /* * The action to take when the trigger fires. The values are explained - * in Table~\ref{tab:action}. + * in xref:tab:action[]. */ -#define CSR_ICOUNT_ACTION_OFFSET 0 -#define CSR_ICOUNT_ACTION_LENGTH 6 -#define CSR_ICOUNT_ACTION 0x3f +#define CSR_ICOUNT_ACTION_OFFSET 0ULL +#define CSR_ICOUNT_ACTION_LENGTH 6ULL +#define CSR_ICOUNT_ACTION 0x3fULL /* * breakpoint: */ @@ -1400,12 +1566,12 @@ */ #define CSR_ICOUNT_ACTION_EXTERNAL1 9 #define CSR_ITRIGGER 0x7a1 -#define CSR_ITRIGGER_TYPE_OFFSET(XLEN) (XLEN + -4) -#define CSR_ITRIGGER_TYPE_LENGTH 4 -#define CSR_ITRIGGER_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_ITRIGGER_DMODE_OFFSET(XLEN) (XLEN + -5) -#define CSR_ITRIGGER_DMODE_LENGTH 1 -#define CSR_ITRIGGER_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_ITRIGGER_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL) +#define CSR_ITRIGGER_TYPE_LENGTH 4ULL +#define CSR_ITRIGGER_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL))) +#define CSR_ITRIGGER_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL) +#define CSR_ITRIGGER_DMODE_LENGTH 1ULL +#define CSR_ITRIGGER_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL)) /* * If this bit is implemented, the hardware sets it when this * trigger matches. The trigger's user can set or clear it at any @@ -1413,66 +1579,66 @@ * trigger(s) matched. If the bit is not implemented, it is always 0 * and writing it has no effect. */ -#define CSR_ITRIGGER_HIT_OFFSET(XLEN) (XLEN + -6) -#define CSR_ITRIGGER_HIT_LENGTH 1 -#define CSR_ITRIGGER_HIT(XLEN) (1ULL<<(XLEN + -6)) +#define CSR_ITRIGGER_HIT_OFFSET(XLEN) ((XLEN) + -6ULL) +#define CSR_ITRIGGER_HIT_LENGTH 1ULL +#define CSR_ITRIGGER_HIT(XLEN) (1ULL << ((XLEN) + -6ULL)) /* * When set, enable this trigger for interrupts that are taken from VS * mode. * This bit is hard-wired to 0 if the hart does not support * virtualization mode. */ -#define CSR_ITRIGGER_VS_OFFSET 0xc -#define CSR_ITRIGGER_VS_LENGTH 1 -#define CSR_ITRIGGER_VS 0x1000 +#define CSR_ITRIGGER_VS_OFFSET 0xcULL +#define CSR_ITRIGGER_VS_LENGTH 1ULL +#define CSR_ITRIGGER_VS 0x1000ULL /* * When set, enable this trigger for interrupts that are taken from VU * mode. * This bit is hard-wired to 0 if the hart does not support * virtualization mode. */ -#define CSR_ITRIGGER_VU_OFFSET 0xb -#define CSR_ITRIGGER_VU_LENGTH 1 -#define CSR_ITRIGGER_VU 0x800 +#define CSR_ITRIGGER_VU_OFFSET 0xbULL +#define CSR_ITRIGGER_VU_LENGTH 1ULL +#define CSR_ITRIGGER_VU 0x800ULL /* * When set, non-maskable interrupts cause this * trigger to fire if the trigger is enabled for the current mode. */ -#define CSR_ITRIGGER_NMI_OFFSET 0xa -#define CSR_ITRIGGER_NMI_LENGTH 1 -#define CSR_ITRIGGER_NMI 0x400 +#define CSR_ITRIGGER_NMI_OFFSET 0xaULL +#define CSR_ITRIGGER_NMI_LENGTH 1ULL +#define CSR_ITRIGGER_NMI 0x400ULL /* * When set, enable this trigger for interrupts that are taken from M * mode. */ -#define CSR_ITRIGGER_M_OFFSET 9 -#define CSR_ITRIGGER_M_LENGTH 1 -#define CSR_ITRIGGER_M 0x200 +#define CSR_ITRIGGER_M_OFFSET 9ULL +#define CSR_ITRIGGER_M_LENGTH 1ULL +#define CSR_ITRIGGER_M 0x200ULL /* * When set, enable this trigger for interrupts that are taken from S/HS * mode. * This bit is hard-wired to 0 if the hart does not support * S-mode. */ -#define CSR_ITRIGGER_S_OFFSET 7 -#define CSR_ITRIGGER_S_LENGTH 1 -#define CSR_ITRIGGER_S 0x80 +#define CSR_ITRIGGER_S_OFFSET 7ULL +#define CSR_ITRIGGER_S_LENGTH 1ULL +#define CSR_ITRIGGER_S 0x80ULL /* * When set, enable this trigger for interrupts that are taken from U * mode. * This bit is hard-wired to 0 if the hart does not support * U-mode. */ -#define CSR_ITRIGGER_U_OFFSET 6 -#define CSR_ITRIGGER_U_LENGTH 1 -#define CSR_ITRIGGER_U 0x40 +#define CSR_ITRIGGER_U_OFFSET 6ULL +#define CSR_ITRIGGER_U_LENGTH 1ULL +#define CSR_ITRIGGER_U 0x40ULL /* * The action to take when the trigger fires. The values are explained - * in Table~\ref{tab:action}. + * in xref:tab:action[]. */ -#define CSR_ITRIGGER_ACTION_OFFSET 0 -#define CSR_ITRIGGER_ACTION_LENGTH 6 -#define CSR_ITRIGGER_ACTION 0x3f +#define CSR_ITRIGGER_ACTION_OFFSET 0ULL +#define CSR_ITRIGGER_ACTION_LENGTH 6ULL +#define CSR_ITRIGGER_ACTION 0x3fULL /* * breakpoint: */ @@ -1502,12 +1668,12 @@ */ #define CSR_ITRIGGER_ACTION_EXTERNAL1 9 #define CSR_ETRIGGER 0x7a1 -#define CSR_ETRIGGER_TYPE_OFFSET(XLEN) (XLEN + -4) -#define CSR_ETRIGGER_TYPE_LENGTH 4 -#define CSR_ETRIGGER_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_ETRIGGER_DMODE_OFFSET(XLEN) (XLEN + -5) -#define CSR_ETRIGGER_DMODE_LENGTH 1 -#define CSR_ETRIGGER_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_ETRIGGER_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL) +#define CSR_ETRIGGER_TYPE_LENGTH 4ULL +#define CSR_ETRIGGER_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL))) +#define CSR_ETRIGGER_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL) +#define CSR_ETRIGGER_DMODE_LENGTH 1ULL +#define CSR_ETRIGGER_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL)) /* * If this bit is implemented, the hardware sets it when this * trigger matches. The trigger's user can set or clear it at any @@ -1515,59 +1681,59 @@ * trigger(s) matched. If the bit is not implemented, it is always 0 * and writing it has no effect. */ -#define CSR_ETRIGGER_HIT_OFFSET(XLEN) (XLEN + -6) -#define CSR_ETRIGGER_HIT_LENGTH 1 -#define CSR_ETRIGGER_HIT(XLEN) (1ULL<<(XLEN + -6)) +#define CSR_ETRIGGER_HIT_OFFSET(XLEN) ((XLEN) + -6ULL) +#define CSR_ETRIGGER_HIT_LENGTH 1ULL +#define CSR_ETRIGGER_HIT(XLEN) (1ULL << ((XLEN) + -6ULL)) /* * When set, enable this trigger for exceptions that are taken from VS * mode. * This bit is hard-wired to 0 if the hart does not support * virtualization mode. */ -#define CSR_ETRIGGER_VS_OFFSET 0xc -#define CSR_ETRIGGER_VS_LENGTH 1 -#define CSR_ETRIGGER_VS 0x1000 +#define CSR_ETRIGGER_VS_OFFSET 0xcULL +#define CSR_ETRIGGER_VS_LENGTH 1ULL +#define CSR_ETRIGGER_VS 0x1000ULL /* * When set, enable this trigger for exceptions that are taken from VU * mode. * This bit is hard-wired to 0 if the hart does not support * virtualization mode. */ -#define CSR_ETRIGGER_VU_OFFSET 0xb -#define CSR_ETRIGGER_VU_LENGTH 1 -#define CSR_ETRIGGER_VU 0x800 +#define CSR_ETRIGGER_VU_OFFSET 0xbULL +#define CSR_ETRIGGER_VU_LENGTH 1ULL +#define CSR_ETRIGGER_VU 0x800ULL /* * When set, enable this trigger for exceptions that are taken from M * mode. */ -#define CSR_ETRIGGER_M_OFFSET 9 -#define CSR_ETRIGGER_M_LENGTH 1 -#define CSR_ETRIGGER_M 0x200 +#define CSR_ETRIGGER_M_OFFSET 9ULL +#define CSR_ETRIGGER_M_LENGTH 1ULL +#define CSR_ETRIGGER_M 0x200ULL /* * When set, enable this trigger for exceptions that are taken from S/HS * mode. * This bit is hard-wired to 0 if the hart does not support * S-mode. */ -#define CSR_ETRIGGER_S_OFFSET 7 -#define CSR_ETRIGGER_S_LENGTH 1 -#define CSR_ETRIGGER_S 0x80 +#define CSR_ETRIGGER_S_OFFSET 7ULL +#define CSR_ETRIGGER_S_LENGTH 1ULL +#define CSR_ETRIGGER_S 0x80ULL /* * When set, enable this trigger for exceptions that are taken from U * mode. * This bit is hard-wired to 0 if the hart does not support * U-mode. */ -#define CSR_ETRIGGER_U_OFFSET 6 -#define CSR_ETRIGGER_U_LENGTH 1 -#define CSR_ETRIGGER_U 0x40 +#define CSR_ETRIGGER_U_OFFSET 6ULL +#define CSR_ETRIGGER_U_LENGTH 1ULL +#define CSR_ETRIGGER_U 0x40ULL /* * The action to take when the trigger fires. The values are explained - * in Table~\ref{tab:action}. + * in xref:tab:action[]. */ -#define CSR_ETRIGGER_ACTION_OFFSET 0 -#define CSR_ETRIGGER_ACTION_LENGTH 6 -#define CSR_ETRIGGER_ACTION 0x3f +#define CSR_ETRIGGER_ACTION_OFFSET 0ULL +#define CSR_ETRIGGER_ACTION_LENGTH 6ULL +#define CSR_ETRIGGER_ACTION 0x3fULL /* * breakpoint: */ @@ -1597,12 +1763,12 @@ */ #define CSR_ETRIGGER_ACTION_EXTERNAL1 9 #define CSR_TMEXTTRIGGER 0x7a1 -#define CSR_TMEXTTRIGGER_TYPE_OFFSET(XLEN) (XLEN + -4) -#define CSR_TMEXTTRIGGER_TYPE_LENGTH 4 -#define CSR_TMEXTTRIGGER_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4))) -#define CSR_TMEXTTRIGGER_DMODE_OFFSET(XLEN) (XLEN + -5) -#define CSR_TMEXTTRIGGER_DMODE_LENGTH 1 -#define CSR_TMEXTTRIGGER_DMODE(XLEN) (1ULL<<(XLEN + -5)) +#define CSR_TMEXTTRIGGER_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL) +#define CSR_TMEXTTRIGGER_TYPE_LENGTH 4ULL +#define CSR_TMEXTTRIGGER_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL))) +#define CSR_TMEXTTRIGGER_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL) +#define CSR_TMEXTTRIGGER_DMODE_LENGTH 1ULL +#define CSR_TMEXTTRIGGER_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL)) /* * If this bit is implemented, the hardware sets it when this * trigger matches. The trigger's user can set or clear it at any @@ -1610,30 +1776,30 @@ * trigger(s) matched. If the bit is not implemented, it is always 0 * and writing it has no effect. */ -#define CSR_TMEXTTRIGGER_HIT_OFFSET(XLEN) (XLEN + -6) -#define CSR_TMEXTTRIGGER_HIT_LENGTH 1 -#define CSR_TMEXTTRIGGER_HIT(XLEN) (1ULL<<(XLEN + -6)) +#define CSR_TMEXTTRIGGER_HIT_OFFSET(XLEN) ((XLEN) + -6ULL) +#define CSR_TMEXTTRIGGER_HIT_LENGTH 1ULL +#define CSR_TMEXTTRIGGER_HIT(XLEN) (1ULL << ((XLEN) + -6ULL)) /* * This optional bit, when set, causes this trigger to fire whenever an attached * interrupt controller signals a trigger. */ -#define CSR_TMEXTTRIGGER_INTCTL_OFFSET 0x16 -#define CSR_TMEXTTRIGGER_INTCTL_LENGTH 1 -#define CSR_TMEXTTRIGGER_INTCTL 0x400000 +#define CSR_TMEXTTRIGGER_INTCTL_OFFSET 0x16ULL +#define CSR_TMEXTTRIGGER_INTCTL_LENGTH 1ULL +#define CSR_TMEXTTRIGGER_INTCTL 0x400000ULL /* - * Selects any combination of up to 16 external debug trigger inputs + * Selects any combination of up to 16 TM external trigger inputs * that cause this trigger to fire. */ -#define CSR_TMEXTTRIGGER_SELECT_OFFSET 6 -#define CSR_TMEXTTRIGGER_SELECT_LENGTH 0x10 -#define CSR_TMEXTTRIGGER_SELECT 0x3fffc0 +#define CSR_TMEXTTRIGGER_SELECT_OFFSET 6ULL +#define CSR_TMEXTTRIGGER_SELECT_LENGTH 0x10ULL +#define CSR_TMEXTTRIGGER_SELECT 0x3fffc0ULL /* * The action to take when the trigger fires. The values are explained - * in Table~\ref{tab:action}. + * in xref:tab:action[]. */ -#define CSR_TMEXTTRIGGER_ACTION_OFFSET 0 -#define CSR_TMEXTTRIGGER_ACTION_LENGTH 6 -#define CSR_TMEXTTRIGGER_ACTION 0x3f +#define CSR_TMEXTTRIGGER_ACTION_OFFSET 0ULL +#define CSR_TMEXTTRIGGER_ACTION_LENGTH 6ULL +#define CSR_TMEXTTRIGGER_ACTION 0x3fULL /* * breakpoint: */ @@ -1664,29 +1830,31 @@ #define CSR_TMEXTTRIGGER_ACTION_EXTERNAL1 9 #define CSR_TEXTRA32 0x7a3 /* - * Data used together with \FcsrTextraThirtytwoMhselect. + * Data used together with {textra32-mhselect}. */ -#define CSR_TEXTRA32_MHVALUE_OFFSET 0x1a -#define CSR_TEXTRA32_MHVALUE_LENGTH 6 -#define CSR_TEXTRA32_MHVALUE 0xfc000000U -#define CSR_TEXTRA32_MHSELECT_OFFSET 0x17 -#define CSR_TEXTRA32_MHSELECT_LENGTH 3 -#define CSR_TEXTRA32_MHSELECT 0x3800000 +#define CSR_TEXTRA32_MHVALUE_OFFSET 0x1aULL +#define CSR_TEXTRA32_MHVALUE_LENGTH 6ULL +#define CSR_TEXTRA32_MHVALUE 0xfc000000ULL +#define CSR_TEXTRA32_MHSELECT_OFFSET 0x17ULL +#define CSR_TEXTRA32_MHSELECT_LENGTH 3ULL +#define CSR_TEXTRA32_MHSELECT 0x3800000ULL /* - * ignore: Ignore \FcsrTextraThirtytwoMhvalue. + * ignore: Ignore {textra32-mhvalue}. */ #define CSR_TEXTRA32_MHSELECT_IGNORE 0 /* - * mcontext: This trigger will only match if the low bits of - * \RcsrMcontext/\RcsrHcontext equal \FcsrTextraThirtytwoMhvalue. + * mcontext: This trigger will only match or fire if the low bits of + * {csr-mcontext}/{csr-hcontext} equal {textra32-mhvalue}. */ #define CSR_TEXTRA32_MHSELECT_MCONTEXT 4 /* - * 1, 5 (mcontext\_select): This trigger will only match if the low bits of - * \RcsrMcontext/\RcsrHcontext equal \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}. + * 1, 5 (mcontext_select): This trigger will only match or fire if the + * low bits of + * {csr-mcontext}/{csr-hcontext} equal {{textra32-mhvalue}, mhselect[2]}. * - * 2, 6 (vmid\_select): This trigger will only match if VMID in hgatp equals the lower VMIDMAX - * (defined in the Privileged Spec) bits of \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}. + * 2, 6 (vmid_select): This trigger will only match or fire if VMID in + * hgatp equals the lower VMIDMAX + * (defined in the Privileged Spec) bits of {{textra32-mhvalue}, mhselect[2]}. * * 3, 7 (reserved): Reserved. * @@ -1694,193 +1862,192 @@ */ /* * When the least significant bit of this field is 1, it causes bits 7:0 - * in the comparison to be ignored, when \FcsrTextraThirtytwoSselect=1. + * in the comparison to be ignored, when {textra32-sselect}=1. * When the next most significant bit of this field is 1, it causes bits 15:8 - * to be ignored in the comparison, when \FcsrTextraThirtytwoSselect=1. + * to be ignored in the comparison, when {textra32-sselect}=1. */ -#define CSR_TEXTRA32_SBYTEMASK_OFFSET 0x12 -#define CSR_TEXTRA32_SBYTEMASK_LENGTH 2 -#define CSR_TEXTRA32_SBYTEMASK 0xc0000 +#define CSR_TEXTRA32_SBYTEMASK_OFFSET 0x12ULL +#define CSR_TEXTRA32_SBYTEMASK_LENGTH 2ULL +#define CSR_TEXTRA32_SBYTEMASK 0xc0000ULL /* - * Data used together with \FcsrTextraThirtytwoSselect. + * Data used together with {textra32-sselect}. * * This field should be tied to 0 when S-mode is not supported. */ -#define CSR_TEXTRA32_SVALUE_OFFSET 2 -#define CSR_TEXTRA32_SVALUE_LENGTH 0x10 -#define CSR_TEXTRA32_SVALUE 0x3fffc -#define CSR_TEXTRA32_SSELECT_OFFSET 0 -#define CSR_TEXTRA32_SSELECT_LENGTH 2 -#define CSR_TEXTRA32_SSELECT 3 +#define CSR_TEXTRA32_SVALUE_OFFSET 2ULL +#define CSR_TEXTRA32_SVALUE_LENGTH 0x10ULL +#define CSR_TEXTRA32_SVALUE 0x3fffcULL +#define CSR_TEXTRA32_SSELECT_OFFSET 0ULL +#define CSR_TEXTRA32_SSELECT_LENGTH 2ULL +#define CSR_TEXTRA32_SSELECT 3ULL /* - * ignore: Ignore \FcsrTextraThirtytwoSvalue. + * ignore: Ignore {textra32-svalue}. */ #define CSR_TEXTRA32_SSELECT_IGNORE 0 /* - * scontext: This trigger will only match if the low bits of - * \RcsrScontext equal \FcsrTextraThirtytwoSvalue. + * scontext: This trigger will only match or fire if the low bits of + * {csr-scontext} equal {textra32-svalue}. */ #define CSR_TEXTRA32_SSELECT_SCONTEXT 1 /* - * asid: This trigger will only match if: - * \begin{itemize}[noitemsep,nolistsep] - * \item the mode is VS-mode or VU-mode and ASID in \Rvsatp + * asid: This trigger will only match or fire if: + * + * * the mode is VS-mode or VU-mode and ASID in `vsatp` * equals the lower ASIDMAX (defined in the Privileged Spec) bits - * of \FcsrTextraThirtytwoSvalue. - * \item in all other modes, ASID in \Rsatp equals the lower + * of {textra32-svalue}. + * + * * in all other modes, ASID in `satp` equals the lower * ASIDMAX (defined in the Privileged Spec) bits of - * \FcsrTextraThirtytwoSvalue. - * \end{itemize} + * {textra32-svalue}. */ #define CSR_TEXTRA32_SSELECT_ASID 2 /* * This field should be tied to 0 when S-mode is not supported. */ #define CSR_TEXTRA64 0x7a3 -#define CSR_TEXTRA64_MHVALUE_OFFSET 0x33 -#define CSR_TEXTRA64_MHVALUE_LENGTH 0xd +#define CSR_TEXTRA64_MHVALUE_OFFSET 0x33ULL +#define CSR_TEXTRA64_MHVALUE_LENGTH 0xdULL #define CSR_TEXTRA64_MHVALUE 0xfff8000000000000ULL -#define CSR_TEXTRA64_MHSELECT_OFFSET 0x30 -#define CSR_TEXTRA64_MHSELECT_LENGTH 3 +#define CSR_TEXTRA64_MHSELECT_OFFSET 0x30ULL +#define CSR_TEXTRA64_MHSELECT_LENGTH 3ULL #define CSR_TEXTRA64_MHSELECT 0x7000000000000ULL /* * When the least significant bit of this field is 1, it causes bits 7:0 - * in the comparison to be ignored, when \FcsrTextraSixtyfourSselect=1. + * in the comparison to be ignored, when {textra64-sselect}=1. * Likewise, the second bit controls the comparison of bits 15:8, * third bit controls the comparison of bits 23:16, - * fourth bit controls the comparison of bits 31:24, and - * fifth bit controls the comparison of bits 33:32. + * and fourth bit controls the comparison of bits 31:24. */ -#define CSR_TEXTRA64_SBYTEMASK_OFFSET 0x24 -#define CSR_TEXTRA64_SBYTEMASK_LENGTH 5 -#define CSR_TEXTRA64_SBYTEMASK 0x1f000000000ULL -#define CSR_TEXTRA64_SVALUE_OFFSET 2 -#define CSR_TEXTRA64_SVALUE_LENGTH 0x22 -#define CSR_TEXTRA64_SVALUE 0xffffffffcULL -#define CSR_TEXTRA64_SSELECT_OFFSET 0 -#define CSR_TEXTRA64_SSELECT_LENGTH 2 -#define CSR_TEXTRA64_SSELECT 3 +#define CSR_TEXTRA64_SBYTEMASK_OFFSET 0x24ULL +#define CSR_TEXTRA64_SBYTEMASK_LENGTH 4ULL +#define CSR_TEXTRA64_SBYTEMASK 0xf000000000ULL +#define CSR_TEXTRA64_SVALUE_OFFSET 2ULL +#define CSR_TEXTRA64_SVALUE_LENGTH 0x20ULL +#define CSR_TEXTRA64_SVALUE 0x3fffffffcULL +#define CSR_TEXTRA64_SSELECT_OFFSET 0ULL +#define CSR_TEXTRA64_SSELECT_LENGTH 2ULL +#define CSR_TEXTRA64_SSELECT 3ULL #define DM_DMSTATUS 0x11 -#define DM_DMSTATUS_NDMRESETPENDING_OFFSET 0x18 -#define DM_DMSTATUS_NDMRESETPENDING_LENGTH 1 -#define DM_DMSTATUS_NDMRESETPENDING 0x1000000 +#define DM_DMSTATUS_NDMRESETPENDING_OFFSET 0x18ULL +#define DM_DMSTATUS_NDMRESETPENDING_LENGTH 1ULL +#define DM_DMSTATUS_NDMRESETPENDING 0x1000000ULL /* - * false: Unimplemented, or \FdmDmcontrolNdmreset is zero and no ndmreset is currently + * false: Unimplemented, or {dmcontrol-ndmreset} is zero and no ndmreset is currently * in progress. */ #define DM_DMSTATUS_NDMRESETPENDING_FALSE 0 /* - * true: \FdmDmcontrolNdmreset is currently nonzero, or there is an ndmreset in progress. + * true: {dmcontrol-ndmreset} is currently nonzero, or there is an ndmreset in progress. */ #define DM_DMSTATUS_NDMRESETPENDING_TRUE 1 -#define DM_DMSTATUS_STICKYUNAVAIL_OFFSET 0x17 -#define DM_DMSTATUS_STICKYUNAVAIL_LENGTH 1 -#define DM_DMSTATUS_STICKYUNAVAIL 0x800000 +#define DM_DMSTATUS_STICKYUNAVAIL_OFFSET 0x17ULL +#define DM_DMSTATUS_STICKYUNAVAIL_LENGTH 1ULL +#define DM_DMSTATUS_STICKYUNAVAIL 0x800000ULL /* - * current: The per-hart {\tt unavail} bits reflect the current state of the hart. + * current: The per-hart `unavail` bits reflect the current state of the hart. */ #define DM_DMSTATUS_STICKYUNAVAIL_CURRENT 0 /* - * sticky: The per-hart {\tt unavail} bits are sticky. Once they are set, they will - * not clear until the debugger acknowledges them using \FdmDmcontrolAckunavail. + * sticky: The per-hart `unavail` bits are sticky. Once they are set, they will + * not clear until the debugger acknowledges them using {dmcontrol-ackunavail}. */ #define DM_DMSTATUS_STICKYUNAVAIL_STICKY 1 /* - * If 1, then there is an implicit {\tt ebreak} instruction at the + * If 1, then there is an implicit `ebreak` instruction at the * non-existent word immediately after the Program Buffer. This saves - * the debugger from having to write the {\tt ebreak} itself, and + * the debugger from having to write the `ebreak` itself, and * allows the Program Buffer to be one word smaller. * - * This must be 1 when \FdmAbstractcsProgbufsize is 1. + * This must be 1 when {abstractcs-progbufsize} is 1. */ -#define DM_DMSTATUS_IMPEBREAK_OFFSET 0x16 -#define DM_DMSTATUS_IMPEBREAK_LENGTH 1 -#define DM_DMSTATUS_IMPEBREAK 0x400000 +#define DM_DMSTATUS_IMPEBREAK_OFFSET 0x16ULL +#define DM_DMSTATUS_IMPEBREAK_LENGTH 1ULL +#define DM_DMSTATUS_IMPEBREAK 0x400000ULL /* * This field is 1 when all currently selected harts have been reset * and reset has not been acknowledged for any of them. */ -#define DM_DMSTATUS_ALLHAVERESET_OFFSET 0x13 -#define DM_DMSTATUS_ALLHAVERESET_LENGTH 1 -#define DM_DMSTATUS_ALLHAVERESET 0x80000 +#define DM_DMSTATUS_ALLHAVERESET_OFFSET 0x13ULL +#define DM_DMSTATUS_ALLHAVERESET_LENGTH 1ULL +#define DM_DMSTATUS_ALLHAVERESET 0x80000ULL /* * This field is 1 when at least one currently selected hart has been * reset and reset has not been acknowledged for that hart. */ -#define DM_DMSTATUS_ANYHAVERESET_OFFSET 0x12 -#define DM_DMSTATUS_ANYHAVERESET_LENGTH 1 -#define DM_DMSTATUS_ANYHAVERESET 0x40000 +#define DM_DMSTATUS_ANYHAVERESET_OFFSET 0x12ULL +#define DM_DMSTATUS_ANYHAVERESET_LENGTH 1ULL +#define DM_DMSTATUS_ANYHAVERESET 0x40000ULL /* * This field is 1 when all currently selected harts have their - * resume ack bit\index{resume ack bit} set. + * ((resume ack bit)) set. */ -#define DM_DMSTATUS_ALLRESUMEACK_OFFSET 0x11 -#define DM_DMSTATUS_ALLRESUMEACK_LENGTH 1 -#define DM_DMSTATUS_ALLRESUMEACK 0x20000 +#define DM_DMSTATUS_ALLRESUMEACK_OFFSET 0x11ULL +#define DM_DMSTATUS_ALLRESUMEACK_LENGTH 1ULL +#define DM_DMSTATUS_ALLRESUMEACK 0x20000ULL /* * This field is 1 when any currently selected hart has its - * resume ack bit\index{resume ack bit} set. + * ((resume ack bit)) set. */ -#define DM_DMSTATUS_ANYRESUMEACK_OFFSET 0x10 -#define DM_DMSTATUS_ANYRESUMEACK_LENGTH 1 -#define DM_DMSTATUS_ANYRESUMEACK 0x10000 +#define DM_DMSTATUS_ANYRESUMEACK_OFFSET 0x10ULL +#define DM_DMSTATUS_ANYRESUMEACK_LENGTH 1ULL +#define DM_DMSTATUS_ANYRESUMEACK 0x10000ULL /* * This field is 1 when all currently selected harts do not exist in * this hardware platform. */ -#define DM_DMSTATUS_ALLNONEXISTENT_OFFSET 0xf -#define DM_DMSTATUS_ALLNONEXISTENT_LENGTH 1 -#define DM_DMSTATUS_ALLNONEXISTENT 0x8000 +#define DM_DMSTATUS_ALLNONEXISTENT_OFFSET 0xfULL +#define DM_DMSTATUS_ALLNONEXISTENT_LENGTH 1ULL +#define DM_DMSTATUS_ALLNONEXISTENT 0x8000ULL /* * This field is 1 when any currently selected hart does not exist in * this hardware platform. */ -#define DM_DMSTATUS_ANYNONEXISTENT_OFFSET 0xe -#define DM_DMSTATUS_ANYNONEXISTENT_LENGTH 1 -#define DM_DMSTATUS_ANYNONEXISTENT 0x4000 +#define DM_DMSTATUS_ANYNONEXISTENT_OFFSET 0xeULL +#define DM_DMSTATUS_ANYNONEXISTENT_LENGTH 1ULL +#define DM_DMSTATUS_ANYNONEXISTENT 0x4000ULL /* * This field is 1 when all currently selected harts are - * unavailable, or (if \FdmDmstatusStickyunavail is 1) were + * unavailable, or (if {dmstatus-stickyunavail} is 1) were * unavailable without that being acknowledged. */ -#define DM_DMSTATUS_ALLUNAVAIL_OFFSET 0xd -#define DM_DMSTATUS_ALLUNAVAIL_LENGTH 1 -#define DM_DMSTATUS_ALLUNAVAIL 0x2000 +#define DM_DMSTATUS_ALLUNAVAIL_OFFSET 0xdULL +#define DM_DMSTATUS_ALLUNAVAIL_LENGTH 1ULL +#define DM_DMSTATUS_ALLUNAVAIL 0x2000ULL /* * This field is 1 when any currently selected hart is unavailable, - * or (if \FdmDmstatusStickyunavail is 1) was unavailable without + * or (if {dmstatus-stickyunavail} is 1) was unavailable without * that being acknowledged. */ -#define DM_DMSTATUS_ANYUNAVAIL_OFFSET 0xc -#define DM_DMSTATUS_ANYUNAVAIL_LENGTH 1 -#define DM_DMSTATUS_ANYUNAVAIL 0x1000 +#define DM_DMSTATUS_ANYUNAVAIL_OFFSET 0xcULL +#define DM_DMSTATUS_ANYUNAVAIL_LENGTH 1ULL +#define DM_DMSTATUS_ANYUNAVAIL 0x1000ULL /* * This field is 1 when all currently selected harts are running. */ -#define DM_DMSTATUS_ALLRUNNING_OFFSET 0xb -#define DM_DMSTATUS_ALLRUNNING_LENGTH 1 -#define DM_DMSTATUS_ALLRUNNING 0x800 +#define DM_DMSTATUS_ALLRUNNING_OFFSET 0xbULL +#define DM_DMSTATUS_ALLRUNNING_LENGTH 1ULL +#define DM_DMSTATUS_ALLRUNNING 0x800ULL /* * This field is 1 when any currently selected hart is running. */ -#define DM_DMSTATUS_ANYRUNNING_OFFSET 0xa -#define DM_DMSTATUS_ANYRUNNING_LENGTH 1 -#define DM_DMSTATUS_ANYRUNNING 0x400 +#define DM_DMSTATUS_ANYRUNNING_OFFSET 0xaULL +#define DM_DMSTATUS_ANYRUNNING_LENGTH 1ULL +#define DM_DMSTATUS_ANYRUNNING 0x400ULL /* * This field is 1 when all currently selected harts are halted. */ -#define DM_DMSTATUS_ALLHALTED_OFFSET 9 -#define DM_DMSTATUS_ALLHALTED_LENGTH 1 -#define DM_DMSTATUS_ALLHALTED 0x200 +#define DM_DMSTATUS_ALLHALTED_OFFSET 9ULL +#define DM_DMSTATUS_ALLHALTED_LENGTH 1ULL +#define DM_DMSTATUS_ALLHALTED 0x200ULL /* * This field is 1 when any currently selected hart is halted. */ -#define DM_DMSTATUS_ANYHALTED_OFFSET 8 -#define DM_DMSTATUS_ANYHALTED_LENGTH 1 -#define DM_DMSTATUS_ANYHALTED 0x100 -#define DM_DMSTATUS_AUTHENTICATED_OFFSET 7 -#define DM_DMSTATUS_AUTHENTICATED_LENGTH 1 -#define DM_DMSTATUS_AUTHENTICATED 0x80 +#define DM_DMSTATUS_ANYHALTED_OFFSET 8ULL +#define DM_DMSTATUS_ANYHALTED_LENGTH 1ULL +#define DM_DMSTATUS_ANYHALTED 0x100ULL +#define DM_DMSTATUS_AUTHENTICATED_OFFSET 7ULL +#define DM_DMSTATUS_AUTHENTICATED_LENGTH 1ULL +#define DM_DMSTATUS_AUTHENTICATED 0x80ULL /* * false: Authentication is required before using the DM. */ @@ -1893,47 +2060,47 @@ * On components that don't implement authentication, this bit must be * preset as 1. */ -#define DM_DMSTATUS_AUTHBUSY_OFFSET 6 -#define DM_DMSTATUS_AUTHBUSY_LENGTH 1 -#define DM_DMSTATUS_AUTHBUSY 0x40 +#define DM_DMSTATUS_AUTHBUSY_OFFSET 6ULL +#define DM_DMSTATUS_AUTHBUSY_LENGTH 1ULL +#define DM_DMSTATUS_AUTHBUSY 0x40ULL /* * ready: The authentication module is ready to process the next - * read/write to \RdmAuthdata. + * read/write to {dm-authdata}. */ #define DM_DMSTATUS_AUTHBUSY_READY 0 /* - * busy: The authentication module is busy. Accessing \RdmAuthdata results + * busy: The authentication module is busy. Accessing {dm-authdata} results * in unspecified behavior. */ #define DM_DMSTATUS_AUTHBUSY_BUSY 1 /* - * \FdmDmstatusAuthbusy only becomes set in immediate response to an access to - * \RdmAuthdata. + * {dmstatus-authbusy} only becomes set in immediate response to an access to + * {dm-authdata}. */ /* * 1 if this Debug Module supports halt-on-reset functionality - * controllable by the \FdmDmcontrolSetresethaltreq and \FdmDmcontrolClrresethaltreq bits. + * controllable by the {dmcontrol-setresethaltreq} and {dmcontrol-clrresethaltreq} bits. * 0 otherwise. */ -#define DM_DMSTATUS_HASRESETHALTREQ_OFFSET 5 -#define DM_DMSTATUS_HASRESETHALTREQ_LENGTH 1 -#define DM_DMSTATUS_HASRESETHALTREQ 0x20 -#define DM_DMSTATUS_CONFSTRPTRVALID_OFFSET 4 -#define DM_DMSTATUS_CONFSTRPTRVALID_LENGTH 1 -#define DM_DMSTATUS_CONFSTRPTRVALID 0x10 +#define DM_DMSTATUS_HASRESETHALTREQ_OFFSET 5ULL +#define DM_DMSTATUS_HASRESETHALTREQ_LENGTH 1ULL +#define DM_DMSTATUS_HASRESETHALTREQ 0x20ULL +#define DM_DMSTATUS_CONFSTRPTRVALID_OFFSET 4ULL +#define DM_DMSTATUS_CONFSTRPTRVALID_LENGTH 1ULL +#define DM_DMSTATUS_CONFSTRPTRVALID 0x10ULL /* - * invalid: \RdmConfstrptrZero--\RdmConfstrptrThree hold information which + * invalid: {dm-confstrptr0}--{dm-confstrptr3} hold information which * is not relevant to the configuration structure. */ #define DM_DMSTATUS_CONFSTRPTRVALID_INVALID 0 /* - * valid: \RdmConfstrptrZero--\RdmConfstrptrThree hold the address of the + * valid: {dm-confstrptr0}--{dm-confstrptr3} hold the address of the * configuration structure. */ #define DM_DMSTATUS_CONFSTRPTRVALID_VALID 1 -#define DM_DMSTATUS_VERSION_OFFSET 0 -#define DM_DMSTATUS_VERSION_LENGTH 4 -#define DM_DMSTATUS_VERSION 0xf +#define DM_DMSTATUS_VERSION_OFFSET 0ULL +#define DM_DMSTATUS_VERSION_LENGTH 4ULL +#define DM_DMSTATUS_VERSION 0xfULL /* * none: There is no Debug Module present. */ @@ -1967,23 +2134,29 @@ * harts. Running harts will halt whenever their halt request bit is * set. * - * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + * Writes apply to the new value of {hartsel} and {dmcontrol-hasel}. + * + * Writes to this bit should be ignored while an abstract command is + * executing. */ -#define DM_DMCONTROL_HALTREQ_OFFSET 0x1f -#define DM_DMCONTROL_HALTREQ_LENGTH 1 -#define DM_DMCONTROL_HALTREQ 0x80000000U +#define DM_DMCONTROL_HALTREQ_OFFSET 0x1fULL +#define DM_DMCONTROL_HALTREQ_LENGTH 1ULL +#define DM_DMCONTROL_HALTREQ 0x80000000ULL /* * Writing 1 causes the currently selected harts to resume once, if * they are halted when the write occurs. It also clears the resume * ack bit for those harts. * - * \FdmDmcontrolResumereq is ignored if \FdmDmcontrolHaltreq is set. + * {dmcontrol-resumereq} is ignored if {dmcontrol-haltreq} is set. * - * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + * Writes apply to the new value of {hartsel} and {dmcontrol-hasel}. + * + * Writes to this bit should be ignored while an abstract command is + * executing. */ -#define DM_DMCONTROL_RESUMEREQ_OFFSET 0x1e -#define DM_DMCONTROL_RESUMEREQ_LENGTH 1 -#define DM_DMCONTROL_RESUMEREQ 0x40000000 +#define DM_DMCONTROL_RESUMEREQ_OFFSET 0x1eULL +#define DM_DMCONTROL_RESUMEREQ_LENGTH 1ULL +#define DM_DMCONTROL_RESUMEREQ 0x40000000ULL /* * This optional field writes the reset bit for all the currently * selected harts. To perform a reset the debugger writes 1, and then @@ -1996,52 +2169,55 @@ * after writing 1 the debugger can read the register back to see if * the feature is supported. * - * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + * Writes apply to the new value of {hartsel} and {dmcontrol-hasel}. */ -#define DM_DMCONTROL_HARTRESET_OFFSET 0x1d -#define DM_DMCONTROL_HARTRESET_LENGTH 1 -#define DM_DMCONTROL_HARTRESET 0x20000000 -#define DM_DMCONTROL_ACKHAVERESET_OFFSET 0x1c -#define DM_DMCONTROL_ACKHAVERESET_LENGTH 1 -#define DM_DMCONTROL_ACKHAVERESET 0x10000000 +#define DM_DMCONTROL_HARTRESET_OFFSET 0x1dULL +#define DM_DMCONTROL_HARTRESET_LENGTH 1ULL +#define DM_DMCONTROL_HARTRESET 0x20000000ULL +#define DM_DMCONTROL_ACKHAVERESET_OFFSET 0x1cULL +#define DM_DMCONTROL_ACKHAVERESET_LENGTH 1ULL +#define DM_DMCONTROL_ACKHAVERESET 0x10000000ULL /* * nop: No effect. */ #define DM_DMCONTROL_ACKHAVERESET_NOP 0 /* - * ack: Clears {\tt havereset} for any selected harts. + * ack: Clears `havereset` for any selected harts. */ #define DM_DMCONTROL_ACKHAVERESET_ACK 1 /* - * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + * Writes apply to the new value of {hartsel} and {dmcontrol-hasel}. + * + * Writes to this bit should be ignored while an abstract command is + * executing. */ -#define DM_DMCONTROL_ACKUNAVAIL_OFFSET 0x1b -#define DM_DMCONTROL_ACKUNAVAIL_LENGTH 1 -#define DM_DMCONTROL_ACKUNAVAIL 0x8000000 +#define DM_DMCONTROL_ACKUNAVAIL_OFFSET 0x1bULL +#define DM_DMCONTROL_ACKUNAVAIL_LENGTH 1ULL +#define DM_DMCONTROL_ACKUNAVAIL 0x8000000ULL /* * nop: No effect. */ #define DM_DMCONTROL_ACKUNAVAIL_NOP 0 /* - * ack: Clears {\tt unavail} for any selected harts that are currently available. + * ack: Clears `unavail` for any selected harts that are currently available. */ #define DM_DMCONTROL_ACKUNAVAIL_ACK 1 /* - * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + * Writes apply to the new value of {hartsel} and {dmcontrol-hasel}. */ /* * Selects the definition of currently selected harts. */ -#define DM_DMCONTROL_HASEL_OFFSET 0x1a -#define DM_DMCONTROL_HASEL_LENGTH 1 -#define DM_DMCONTROL_HASEL 0x4000000 +#define DM_DMCONTROL_HASEL_OFFSET 0x1aULL +#define DM_DMCONTROL_HASEL_LENGTH 1ULL +#define DM_DMCONTROL_HASEL 0x4000000ULL /* - * single: There is a single currently selected hart, that is selected by \Fhartsel. + * single: There is a single currently selected hart, that is selected by {hartsel}. */ #define DM_DMCONTROL_HASEL_SINGLE 0 /* * multiple: There may be multiple currently selected harts -- the hart - * selected by \Fhartsel, plus those selected by the hart array mask + * selected by {hartsel}, plus those selected by the hart array mask * register. */ #define DM_DMCONTROL_HASEL_MULTIPLE 1 @@ -2052,62 +2228,68 @@ * is supported. */ /* - * The low 10 bits of \Fhartsel: the DM-specific index of the hart to + * The low 10 bits of {hartsel}: the DM-specific index of the hart to * select. This hart is always part of the currently selected harts. */ -#define DM_DMCONTROL_HARTSELLO_OFFSET 0x10 -#define DM_DMCONTROL_HARTSELLO_LENGTH 0xa -#define DM_DMCONTROL_HARTSELLO 0x3ff0000 +#define DM_DMCONTROL_HARTSELLO_OFFSET 0x10ULL +#define DM_DMCONTROL_HARTSELLO_LENGTH 0xaULL +#define DM_DMCONTROL_HARTSELLO 0x3ff0000ULL /* - * The high 10 bits of \Fhartsel: the DM-specific index of the hart to + * The high 10 bits of {hartsel}: the DM-specific index of the hart to * select. This hart is always part of the currently selected harts. */ -#define DM_DMCONTROL_HARTSELHI_OFFSET 6 -#define DM_DMCONTROL_HARTSELHI_LENGTH 0xa -#define DM_DMCONTROL_HARTSELHI 0xffc0 +#define DM_DMCONTROL_HARTSELHI_OFFSET 6ULL +#define DM_DMCONTROL_HARTSELHI_LENGTH 0xaULL +#define DM_DMCONTROL_HARTSELHI 0xffc0ULL /* - * This optional field sets \Fkeepalive for all currently selected - * harts, unless \FdmDmcontrolClrkeepalive is simultaneously set to + * This optional field sets {keepalive} for all currently selected + * harts, unless {dmcontrol-clrkeepalive} is simultaneously set to * 1. * - * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + * Writes apply to the new value of {hartsel} and {dmcontrol-hasel}. */ -#define DM_DMCONTROL_SETKEEPALIVE_OFFSET 5 -#define DM_DMCONTROL_SETKEEPALIVE_LENGTH 1 -#define DM_DMCONTROL_SETKEEPALIVE 0x20 +#define DM_DMCONTROL_SETKEEPALIVE_OFFSET 5ULL +#define DM_DMCONTROL_SETKEEPALIVE_LENGTH 1ULL +#define DM_DMCONTROL_SETKEEPALIVE 0x20ULL /* - * This optional field clears \Fkeepalive for all currently selected + * This optional field clears {keepalive} for all currently selected * harts. * - * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + * Writes apply to the new value of {hartsel} and {dmcontrol-hasel}. */ -#define DM_DMCONTROL_CLRKEEPALIVE_OFFSET 4 -#define DM_DMCONTROL_CLRKEEPALIVE_LENGTH 1 -#define DM_DMCONTROL_CLRKEEPALIVE 0x10 +#define DM_DMCONTROL_CLRKEEPALIVE_OFFSET 4ULL +#define DM_DMCONTROL_CLRKEEPALIVE_LENGTH 1ULL +#define DM_DMCONTROL_CLRKEEPALIVE 0x10ULL /* * This optional field writes the halt-on-reset request bit for all - * currently selected harts, unless \FdmDmcontrolClrresethaltreq is + * currently selected harts, unless {dmcontrol-clrresethaltreq} is * simultaneously set to 1. * When set to 1, each selected hart will halt upon the next deassertion * of its reset. The halt-on-reset request bit is not automatically - * cleared. The debugger must write to \FdmDmcontrolClrresethaltreq to clear it. + * cleared. The debugger must write to {dmcontrol-clrresethaltreq} to clear it. * - * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + * Writes apply to the new value of {hartsel} and {dmcontrol-hasel}. * - * If \FdmDmstatusHasresethaltreq is 0, this field is not implemented. + * If {dmstatus-hasresethaltreq} is 0, this field is not implemented. + * + * Writes to this bit should be ignored while an abstract command is + * executing. */ -#define DM_DMCONTROL_SETRESETHALTREQ_OFFSET 3 -#define DM_DMCONTROL_SETRESETHALTREQ_LENGTH 1 -#define DM_DMCONTROL_SETRESETHALTREQ 8 +#define DM_DMCONTROL_SETRESETHALTREQ_OFFSET 3ULL +#define DM_DMCONTROL_SETRESETHALTREQ_LENGTH 1ULL +#define DM_DMCONTROL_SETRESETHALTREQ 8ULL /* * This optional field clears the halt-on-reset request bit for all * currently selected harts. * - * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + * Writes apply to the new value of {hartsel} and {dmcontrol-hasel}. + * + * Writes to this bit should be ignored while an abstract command is + * executing. */ -#define DM_DMCONTROL_CLRRESETHALTREQ_OFFSET 2 -#define DM_DMCONTROL_CLRRESETHALTREQ_LENGTH 1 -#define DM_DMCONTROL_CLRRESETHALTREQ 4 +#define DM_DMCONTROL_CLRRESETHALTREQ_OFFSET 2ULL +#define DM_DMCONTROL_CLRRESETHALTREQ_LENGTH 1ULL +#define DM_DMCONTROL_CLRRESETHALTREQ 4ULL /* * This bit controls the reset signal from the DM to the rest of the * hardware platform. The signal should reset every part of the hardware platform, including @@ -2117,27 +2299,31 @@ * and then writes 0 * to deassert the reset. */ -#define DM_DMCONTROL_NDMRESET_OFFSET 1 -#define DM_DMCONTROL_NDMRESET_LENGTH 1 -#define DM_DMCONTROL_NDMRESET 2 +#define DM_DMCONTROL_NDMRESET_OFFSET 1ULL +#define DM_DMCONTROL_NDMRESET_LENGTH 1ULL +#define DM_DMCONTROL_NDMRESET 2ULL /* * This bit serves as a reset signal for the Debug Module itself. * After changing the value of this bit, the debugger must poll - * \RdmDmcontrol until \FdmDmcontrolDmactive has taken the requested value - * before performing any action that assumes the requested \FdmDmcontrolDmactive + * {dm-dmcontrol} until {dmcontrol-dmactive} has taken the requested value + * before performing any action that assumes the requested {dmcontrol-dmactive} * state change has completed. Hardware may * take an arbitrarily long time to complete activation or deactivation and will - * indicate completion by setting \FdmDmcontrolDmactive to the requested value. + * indicate completion by setting {dmcontrol-dmactive} to the requested value. + * During this time, the DM may ignore any register writes. */ -#define DM_DMCONTROL_DMACTIVE_OFFSET 0 -#define DM_DMCONTROL_DMACTIVE_LENGTH 1 -#define DM_DMCONTROL_DMACTIVE 1 +#define DM_DMCONTROL_DMACTIVE_OFFSET 0ULL +#define DM_DMCONTROL_DMACTIVE_LENGTH 1ULL +#define DM_DMCONTROL_DMACTIVE 1ULL /* * inactive: The module's state, including authentication mechanism, - * takes its reset values (the \FdmDmcontrolDmactive bit is the only bit which can + * takes its reset values (the {dmcontrol-dmactive} bit is the only bit which can * be written to something other than its reset value). Any accesses - * to the module may fail. Specifically, \FdmDmstatusVersion might not return + * to the module may fail. Specifically, {dmstatus-version} might not return * correct data. + * + * When this value is written, the DM may ignore any other bits written + * to {dmcontrol} in the same write. */ #define DM_DMCONTROL_DMACTIVE_INACTIVE 0 /* @@ -2148,9 +2334,9 @@ * No other mechanism should exist that may result in resetting the * Debug Module after power up. * - * To place the Debug Module into a known state, a debugger may write 0 to \FdmDmcontrolDmactive, - * poll until \FdmDmcontrolDmactive is observed 0, write 1 to \FdmDmcontrolDmactive, and - * poll until \FdmDmcontrolDmactive is observed 1. + * To place the Debug Module into a known state, a debugger should write 0 to {dmcontrol-dmactive}, + * poll until {dmcontrol-dmactive} is observed 0, write 1 to {dmcontrol-dmactive}, and + * poll until {dmcontrol-dmactive} is observed 1. * * Implementations may pay attention to this bit to further aid * debugging, for example by preventing the Debug Module from being @@ -2158,80 +2344,76 @@ */ #define DM_HARTINFO 0x12 /* - * Number of {\tt dscratch} registers available for the debugger - * to use during program buffer execution, starting from \RcsrDscratchZero. + * Number of `dscratch` registers available for the debugger + * to use during program buffer execution, starting from {csr-dscratch0}. * The debugger can make no assumptions about the contents of these * registers between commands. */ -#define DM_HARTINFO_NSCRATCH_OFFSET 0x14 -#define DM_HARTINFO_NSCRATCH_LENGTH 4 -#define DM_HARTINFO_NSCRATCH 0xf00000 -#define DM_HARTINFO_DATAACCESS_OFFSET 0x10 -#define DM_HARTINFO_DATAACCESS_LENGTH 1 -#define DM_HARTINFO_DATAACCESS 0x10000 +#define DM_HARTINFO_NSCRATCH_OFFSET 0x14ULL +#define DM_HARTINFO_NSCRATCH_LENGTH 4ULL +#define DM_HARTINFO_NSCRATCH 0xf00000ULL +#define DM_HARTINFO_DATAACCESS_OFFSET 0x10ULL +#define DM_HARTINFO_DATAACCESS_LENGTH 1ULL +#define DM_HARTINFO_DATAACCESS 0x10000ULL /* - * csr: The {\tt data} registers are shadowed in the hart by CSRs. + * csr: The `data` registers are shadowed in the hart by CSRs. * Each CSR is DXLEN bits in size, and corresponds - * to a single argument, per Table~\ref{tab:datareg}. + * to a single argument, per <>. */ #define DM_HARTINFO_DATAACCESS_CSR 0 /* - * memory: The {\tt data} registers are shadowed in the hart's memory map. + * memory: The `data` registers are shadowed in the hart's memory map. * Each register takes up 4 bytes in the memory map. */ #define DM_HARTINFO_DATAACCESS_MEMORY 1 /* - * If \FdmHartinfoDataaccess is 0: Number of CSRs dedicated to - * shadowing the {\tt data} registers. + * If {hartinfo-dataaccess} is 0: Number of CSRs dedicated to + * shadowing the `data` registers. * - * If \FdmHartinfoDataaccess is 1: Number of 32-bit words in the memory map - * dedicated to shadowing the {\tt data} registers. + * If {hartinfo-dataaccess} is 1: Number of 32-bit words in the memory map + * dedicated to shadowing the `data` registers. * - * If this value is non-zero, then the {tt data} registers must go - * beyond being MRs and guarantee they each store a single value, that is - * readable/writable by either side. - * - * Since there are at most 12 {\tt data} registers, the value in this + * Since there are at most 12 `data` registers, the value in this * register must be 12 or smaller. */ -#define DM_HARTINFO_DATASIZE_OFFSET 0xc -#define DM_HARTINFO_DATASIZE_LENGTH 4 -#define DM_HARTINFO_DATASIZE 0xf000 +#define DM_HARTINFO_DATASIZE_OFFSET 0xcULL +#define DM_HARTINFO_DATASIZE_LENGTH 4ULL +#define DM_HARTINFO_DATASIZE 0xf000ULL /* - * If \FdmHartinfoDataaccess is 0: The number of the first CSR dedicated to - * shadowing the {\tt data} registers. + * If {hartinfo-dataaccess} is 0: The number of the first CSR dedicated to + * shadowing the `data` registers. * - * If \FdmHartinfoDataaccess is 1: Address of RAM where the data + * If {hartinfo-dataaccess} is 1: Address of RAM where the data * registers are shadowed. This address is sign extended giving a * range of -2048 to 2047, easily addressed with a load or store using - * \Xzero as the address register. + * `x0` as the address register. */ -#define DM_HARTINFO_DATAADDR_OFFSET 0 -#define DM_HARTINFO_DATAADDR_LENGTH 0xc -#define DM_HARTINFO_DATAADDR 0xfff +#define DM_HARTINFO_DATAADDR_OFFSET 0ULL +#define DM_HARTINFO_DATAADDR_LENGTH 0xcULL +#define DM_HARTINFO_DATAADDR 0xfffULL #define DM_HAWINDOWSEL 0x14 /* * The high bits of this field may be tied to 0, depending on how large - * the array mask register is. E.g.\ on a hardware platform with 48 harts only bit 0 + * the array mask register is. E.g. on a hardware platform with 48 harts only bit 0 * of this field may actually be writable. */ -#define DM_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0 -#define DM_HAWINDOWSEL_HAWINDOWSEL_LENGTH 0xf -#define DM_HAWINDOWSEL_HAWINDOWSEL 0x7fff +#define DM_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0ULL +#define DM_HAWINDOWSEL_HAWINDOWSEL_LENGTH 0xfULL +#define DM_HAWINDOWSEL_HAWINDOWSEL 0x7fffULL #define DM_HAWINDOW 0x15 -#define DM_HAWINDOW_MASKDATA_OFFSET 0 -#define DM_HAWINDOW_MASKDATA_LENGTH 0x20 -#define DM_HAWINDOW_MASKDATA 0xffffffffU +#define DM_HAWINDOW_MASKDATA_OFFSET 0ULL +#define DM_HAWINDOW_MASKDATA_LENGTH 0x20ULL +#define DM_HAWINDOW_MASKDATA 0xffffffffULL #define DM_ABSTRACTCS 0x16 /* * Size of the Program Buffer, in 32-bit words. Valid sizes are 0 - 16. */ -#define DM_ABSTRACTCS_PROGBUFSIZE_OFFSET 0x18 -#define DM_ABSTRACTCS_PROGBUFSIZE_LENGTH 5 -#define DM_ABSTRACTCS_PROGBUFSIZE 0x1f000000 -#define DM_ABSTRACTCS_BUSY_OFFSET 0xc -#define DM_ABSTRACTCS_BUSY_LENGTH 1 -#define DM_ABSTRACTCS_BUSY 0x1000 +#define DM_ABSTRACTCS_PROGBUFSIZE_OFFSET 0x18ULL +#define DM_ABSTRACTCS_PROGBUFSIZE_LENGTH 5ULL +#define DM_ABSTRACTCS_PROGBUFSIZE 0x1f000000ULL +#define DM_ABSTRACTCS_BUSY_OFFSET 0xcULL +#define DM_ABSTRACTCS_BUSY_LENGTH 1ULL +#define DM_ABSTRACTCS_BUSY 0x1000ULL /* * ready: There is no abstract command currently being executed. */ @@ -2241,7 +2423,7 @@ */ #define DM_ABSTRACTCS_BUSY_BUSY 1 /* - * This bit is set as soon as \RdmCommand is written, and is + * This bit is set as soon as {dm-command} is written, and is * not cleared until that command has completed. */ /* @@ -2250,35 +2432,42 @@ * permission checks that apply based on the current architectural * state of the hart performing the access, or with a relaxed set of * permission checks (e.g. PMP restrictions are ignored). The - * details of the latter are implementation-specific. When set to 0, - * full permissions apply; when set to 1, relaxed permissions apply. + * details of the latter are implementation-specific. */ -#define DM_ABSTRACTCS_RELAXEDPRIV_OFFSET 0xb -#define DM_ABSTRACTCS_RELAXEDPRIV_LENGTH 1 -#define DM_ABSTRACTCS_RELAXEDPRIV 0x800 +#define DM_ABSTRACTCS_RELAXEDPRIV_OFFSET 0xbULL +#define DM_ABSTRACTCS_RELAXEDPRIV_LENGTH 1ULL +#define DM_ABSTRACTCS_RELAXEDPRIV 0x800ULL +/* + * full checks: Full permission checks apply. + */ +#define DM_ABSTRACTCS_RELAXEDPRIV_FULL_CHECKS 0 +/* + * relaxed checks: Relaxed permission checks apply. + */ +#define DM_ABSTRACTCS_RELAXEDPRIV_RELAXED_CHECKS 1 /* * Gets set if an abstract command fails. The bits in this field remain set until * they are cleared by writing 1 to them. No abstract command is * started until the value is reset to 0. * - * This field only contains a valid value if \FdmAbstractcsBusy is 0. + * This field only contains a valid value if {abstractcs-busy} is 0. */ -#define DM_ABSTRACTCS_CMDERR_OFFSET 8 -#define DM_ABSTRACTCS_CMDERR_LENGTH 3 -#define DM_ABSTRACTCS_CMDERR 0x700 +#define DM_ABSTRACTCS_CMDERR_OFFSET 8ULL +#define DM_ABSTRACTCS_CMDERR_LENGTH 3ULL +#define DM_ABSTRACTCS_CMDERR 0x700ULL /* * none: No error. */ #define DM_ABSTRACTCS_CMDERR_NONE 0 /* - * busy: An abstract command was executing while \RdmCommand, - * \RdmAbstractcs, or \RdmAbstractauto was written, or when one - * of the {\tt data} or {\tt progbuf} registers was read or written. - * This status is only written if \FdmAbstractcsCmderr contains 0. + * busy: An abstract command was executing while {dm-command}, + * {dm-abstractcs}, or {dm-abstractauto} was written, or when one + * of the `data` or `progbuf` registers was read or written. + * This status is only written if {abstractcs-cmderr} contains 0. */ #define DM_ABSTRACTCS_CMDERR_BUSY 1 /* - * not supported: The command in \RdmCommand is not supported. It + * not supported: The command in {dm-command} is not supported. It * may be supported with different options set, but it will not be * supported at a later time when the hart or system state are * different. @@ -2286,7 +2475,7 @@ #define DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED 2 /* * exception: An exception occurred while executing the command - * (e.g.\ while executing the Program Buffer). + * (e.g. while executing the Program Buffer). */ #define DM_ABSTRACTCS_CMDERR_EXCEPTION 3 /* @@ -2295,7 +2484,7 @@ */ #define DM_ABSTRACTCS_CMDERR_HALT_RESUME 4 /* - * bus: The abstract command failed due to a bus error (e.g.\ + * bus: The abstract command failed due to a bus error (e.g. * alignment, access size, or timeout). */ #define DM_ABSTRACTCS_CMDERR_BUS 5 @@ -2308,70 +2497,70 @@ */ #define DM_ABSTRACTCS_CMDERR_OTHER 7 /* - * Number of {\tt data} registers that are implemented as part of the + * Number of `data` registers that are implemented as part of the * abstract command interface. Valid sizes are 1 -- 12. */ -#define DM_ABSTRACTCS_DATACOUNT_OFFSET 0 -#define DM_ABSTRACTCS_DATACOUNT_LENGTH 4 -#define DM_ABSTRACTCS_DATACOUNT 0xf +#define DM_ABSTRACTCS_DATACOUNT_OFFSET 0ULL +#define DM_ABSTRACTCS_DATACOUNT_LENGTH 4ULL +#define DM_ABSTRACTCS_DATACOUNT 0xfULL #define DM_COMMAND 0x17 /* * The type determines the overall functionality of this * abstract command. */ -#define DM_COMMAND_CMDTYPE_OFFSET 0x18 -#define DM_COMMAND_CMDTYPE_LENGTH 8 -#define DM_COMMAND_CMDTYPE 0xff000000U +#define DM_COMMAND_CMDTYPE_OFFSET 0x18ULL +#define DM_COMMAND_CMDTYPE_LENGTH 8ULL +#define DM_COMMAND_CMDTYPE 0xff000000ULL /* * This field is interpreted in a command-specific manner, * described for each abstract command. */ -#define DM_COMMAND_CONTROL_OFFSET 0 -#define DM_COMMAND_CONTROL_LENGTH 0x18 -#define DM_COMMAND_CONTROL 0xffffff +#define DM_COMMAND_CONTROL_OFFSET 0ULL +#define DM_COMMAND_CONTROL_LENGTH 0x18ULL +#define DM_COMMAND_CONTROL 0xffffffULL #define DM_ABSTRACTAUTO 0x18 /* * When a bit in this field is 1, read or write accesses to the - * corresponding {\tt progbuf} word cause the DM to act as if the - * current value in \RdmCommand was written there again after the - * access to {\tt progbuf} completes. + * corresponding `progbuf` word cause the DM to act as if the + * current value in {dm-command} was written there again after the + * access to `progbuf` completes. */ -#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET 0x10 -#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF_LENGTH 0x10 -#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF 0xffff0000U +#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET 0x10ULL +#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF_LENGTH 0x10ULL +#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF 0xffff0000ULL /* * When a bit in this field is 1, read or write accesses to the - * corresponding {\tt data} word cause the DM to act as if the current - * value in \RdmCommand was written there again after the - * access to {\tt data} completes. + * corresponding `data` word cause the DM to act as if the current + * value in {dm-command} was written there again after the + * access to `data` completes. */ -#define DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET 0 -#define DM_ABSTRACTAUTO_AUTOEXECDATA_LENGTH 0xc -#define DM_ABSTRACTAUTO_AUTOEXECDATA 0xfff +#define DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET 0ULL +#define DM_ABSTRACTAUTO_AUTOEXECDATA_LENGTH 0xcULL +#define DM_ABSTRACTAUTO_AUTOEXECDATA 0xfffULL #define DM_CONFSTRPTR0 0x19 -#define DM_CONFSTRPTR0_ADDR_OFFSET 0 -#define DM_CONFSTRPTR0_ADDR_LENGTH 0x20 -#define DM_CONFSTRPTR0_ADDR 0xffffffffU +#define DM_CONFSTRPTR0_ADDR_OFFSET 0ULL +#define DM_CONFSTRPTR0_ADDR_LENGTH 0x20ULL +#define DM_CONFSTRPTR0_ADDR 0xffffffffULL #define DM_CONFSTRPTR1 0x1a -#define DM_CONFSTRPTR1_ADDR_OFFSET 0 -#define DM_CONFSTRPTR1_ADDR_LENGTH 0x20 -#define DM_CONFSTRPTR1_ADDR 0xffffffffU +#define DM_CONFSTRPTR1_ADDR_OFFSET 0ULL +#define DM_CONFSTRPTR1_ADDR_LENGTH 0x20ULL +#define DM_CONFSTRPTR1_ADDR 0xffffffffULL #define DM_CONFSTRPTR2 0x1b -#define DM_CONFSTRPTR2_ADDR_OFFSET 0 -#define DM_CONFSTRPTR2_ADDR_LENGTH 0x20 -#define DM_CONFSTRPTR2_ADDR 0xffffffffU +#define DM_CONFSTRPTR2_ADDR_OFFSET 0ULL +#define DM_CONFSTRPTR2_ADDR_LENGTH 0x20ULL +#define DM_CONFSTRPTR2_ADDR 0xffffffffULL #define DM_CONFSTRPTR3 0x1c -#define DM_CONFSTRPTR3_ADDR_OFFSET 0 -#define DM_CONFSTRPTR3_ADDR_LENGTH 0x20 -#define DM_CONFSTRPTR3_ADDR 0xffffffffU +#define DM_CONFSTRPTR3_ADDR_OFFSET 0ULL +#define DM_CONFSTRPTR3_ADDR_LENGTH 0x20ULL +#define DM_CONFSTRPTR3_ADDR 0xffffffffULL #define DM_NEXTDM 0x1d -#define DM_NEXTDM_ADDR_OFFSET 0 -#define DM_NEXTDM_ADDR_LENGTH 0x20 -#define DM_NEXTDM_ADDR 0xffffffffU +#define DM_NEXTDM_ADDR_OFFSET 0ULL +#define DM_NEXTDM_ADDR_LENGTH 0x20ULL +#define DM_NEXTDM_ADDR 0xffffffffULL #define DM_DATA0 0x04 -#define DM_DATA0_DATA_OFFSET 0 -#define DM_DATA0_DATA_LENGTH 0x20 -#define DM_DATA0_DATA 0xffffffffU +#define DM_DATA0_DATA_OFFSET 0ULL +#define DM_DATA0_DATA_LENGTH 0x20ULL +#define DM_DATA0_DATA 0xffffffffULL #define DM_DATA1 0x05 #define DM_DATA2 0x06 #define DM_DATA3 0x07 @@ -2384,9 +2573,9 @@ #define DM_DATA10 0x0e #define DM_DATA11 0x0f #define DM_PROGBUF0 0x20 -#define DM_PROGBUF0_DATA_OFFSET 0 -#define DM_PROGBUF0_DATA_LENGTH 0x20 -#define DM_PROGBUF0_DATA 0xffffffffU +#define DM_PROGBUF0_DATA_OFFSET 0ULL +#define DM_PROGBUF0_DATA_LENGTH 0x20ULL +#define DM_PROGBUF0_DATA 0xffffffffULL #define DM_PROGBUF1 0x21 #define DM_PROGBUF2 0x22 #define DM_PROGBUF3 0x23 @@ -2403,13 +2592,13 @@ #define DM_PROGBUF14 0x2e #define DM_PROGBUF15 0x2f #define DM_AUTHDATA 0x30 -#define DM_AUTHDATA_DATA_OFFSET 0 -#define DM_AUTHDATA_DATA_LENGTH 0x20 -#define DM_AUTHDATA_DATA 0xffffffffU +#define DM_AUTHDATA_DATA_OFFSET 0ULL +#define DM_AUTHDATA_DATA_LENGTH 0x20ULL +#define DM_AUTHDATA_DATA 0xffffffffULL #define DM_DMCS2 0x32 -#define DM_DMCS2_GROUPTYPE_OFFSET 0xb -#define DM_DMCS2_GROUPTYPE_LENGTH 1 -#define DM_DMCS2_GROUPTYPE 0x800 +#define DM_DMCS2_GROUPTYPE_OFFSET 0xbULL +#define DM_DMCS2_GROUPTYPE_LENGTH 1ULL +#define DM_DMCS2_GROUPTYPE 0x800ULL /* * halt: The remaining fields in this register configure halt groups. */ @@ -2424,17 +2613,17 @@ * If a non-existent trigger value is written here, the hardware will * change it to a valid one or 0 if no DM external triggers exist. */ -#define DM_DMCS2_DMEXTTRIGGER_OFFSET 7 -#define DM_DMCS2_DMEXTTRIGGER_LENGTH 4 -#define DM_DMCS2_DMEXTTRIGGER 0x780 +#define DM_DMCS2_DMEXTTRIGGER_OFFSET 7ULL +#define DM_DMCS2_DMEXTTRIGGER_LENGTH 4ULL +#define DM_DMCS2_DMEXTTRIGGER 0x780ULL /* - * When \FdmDmcsTwoHgselect is 0, contains the group of the hart - * specified by \Fhartsel. + * When {dmcs2-hgselect} is 0, contains the group of the hart + * specified by {hartsel}. * - * When \FdmDmcsTwoHgselect is 1, contains the group of the DM external - * trigger selected by \FdmDmcsTwoDmexttrigger. + * When {dmcs2-hgselect} is 1, contains the group of the DM external + * trigger selected by {dmcs2-dmexttrigger}. * - * The value written to this field is ignored unless \FdmDmcsTwoHgwrite + * The value written to this field is ignored unless {dmcs2-hgwrite} * is also written 1. * * Group numbers are contiguous starting at 0, with the highest number @@ -2444,30 +2633,30 @@ * * If groups aren't implemented, then this entire field is 0. */ -#define DM_DMCS2_GROUP_OFFSET 2 -#define DM_DMCS2_GROUP_LENGTH 5 -#define DM_DMCS2_GROUP 0x7c +#define DM_DMCS2_GROUP_OFFSET 2ULL +#define DM_DMCS2_GROUP_LENGTH 5ULL +#define DM_DMCS2_GROUP 0x7cULL /* - * When 1 is written and \FdmDmcsTwoHgselect is 0, for every selected - * hart the DM will change its group to the value written to \FdmDmcsTwoGroup, + * When 1 is written and {dmcs2-hgselect} is 0, for every selected + * hart the DM will change its group to the value written to {dmcs2-group}, * if the hardware supports that group for that hart. * Implementations may also change the group of a minimal set of * unselected harts in the same way, if that is necessary due to * a hardware limitation. * - * When 1 is written and \FdmDmcsTwoHgselect is 1, the DM will change - * the group of the DM external trigger selected by \FdmDmcsTwoDmexttrigger - * to the value written to \FdmDmcsTwoGroup, if the hardware supports + * When 1 is written and {dmcs2-hgselect} is 1, the DM will change + * the group of the DM external trigger selected by {dmcs2-dmexttrigger} + * to the value written to {dmcs2-group}, if the hardware supports * that group for that trigger. * * Writing 0 has no effect. */ -#define DM_DMCS2_HGWRITE_OFFSET 1 -#define DM_DMCS2_HGWRITE_LENGTH 1 -#define DM_DMCS2_HGWRITE 2 -#define DM_DMCS2_HGSELECT_OFFSET 0 -#define DM_DMCS2_HGSELECT_LENGTH 1 -#define DM_DMCS2_HGSELECT 1 +#define DM_DMCS2_HGWRITE_OFFSET 1ULL +#define DM_DMCS2_HGWRITE_LENGTH 1ULL +#define DM_DMCS2_HGWRITE 2ULL +#define DM_DMCS2_HGSELECT_OFFSET 0ULL +#define DM_DMCS2_HGSELECT_LENGTH 1ULL +#define DM_DMCS2_HGSELECT 1ULL /* * harts: Operate on harts. */ @@ -2480,25 +2669,25 @@ * If there are no DM external triggers, this field must be tied to 0. */ #define DM_HALTSUM0 0x40 -#define DM_HALTSUM0_HALTSUM0_OFFSET 0 -#define DM_HALTSUM0_HALTSUM0_LENGTH 0x20 -#define DM_HALTSUM0_HALTSUM0 0xffffffffU +#define DM_HALTSUM0_HALTSUM0_OFFSET 0ULL +#define DM_HALTSUM0_HALTSUM0_LENGTH 0x20ULL +#define DM_HALTSUM0_HALTSUM0 0xffffffffULL #define DM_HALTSUM1 0x13 -#define DM_HALTSUM1_HALTSUM1_OFFSET 0 -#define DM_HALTSUM1_HALTSUM1_LENGTH 0x20 -#define DM_HALTSUM1_HALTSUM1 0xffffffffU +#define DM_HALTSUM1_HALTSUM1_OFFSET 0ULL +#define DM_HALTSUM1_HALTSUM1_LENGTH 0x20ULL +#define DM_HALTSUM1_HALTSUM1 0xffffffffULL #define DM_HALTSUM2 0x34 -#define DM_HALTSUM2_HALTSUM2_OFFSET 0 -#define DM_HALTSUM2_HALTSUM2_LENGTH 0x20 -#define DM_HALTSUM2_HALTSUM2 0xffffffffU +#define DM_HALTSUM2_HALTSUM2_OFFSET 0ULL +#define DM_HALTSUM2_HALTSUM2_LENGTH 0x20ULL +#define DM_HALTSUM2_HALTSUM2 0xffffffffULL #define DM_HALTSUM3 0x35 -#define DM_HALTSUM3_HALTSUM3_OFFSET 0 -#define DM_HALTSUM3_HALTSUM3_LENGTH 0x20 -#define DM_HALTSUM3_HALTSUM3 0xffffffffU +#define DM_HALTSUM3_HALTSUM3_OFFSET 0ULL +#define DM_HALTSUM3_HALTSUM3_LENGTH 0x20ULL +#define DM_HALTSUM3_HALTSUM3 0xffffffffULL #define DM_SBCS 0x38 -#define DM_SBCS_SBVERSION_OFFSET 0x1d -#define DM_SBCS_SBVERSION_LENGTH 3 -#define DM_SBCS_SBVERSION 0xe0000000U +#define DM_SBCS_SBVERSION_OFFSET 0x1dULL +#define DM_SBCS_SBVERSION_LENGTH 3ULL +#define DM_SBCS_SBVERSION 0xe0000000ULL /* * legacy: The System Bus interface conforms to mainline drafts of this * spec older than 1 January, 2018. @@ -2514,41 +2703,41 @@ /* * Set when the debugger attempts to read data while a read is in * progress, or when the debugger initiates a new access while one is - * already in progress (while \FdmSbcsSbbusy is set). It remains set until + * already in progress (while {sbcs-sbbusy} is set). It remains set until * it's explicitly cleared by the debugger. * * While this field is set, no more system bus accesses can be * initiated by the Debug Module. */ -#define DM_SBCS_SBBUSYERROR_OFFSET 0x16 -#define DM_SBCS_SBBUSYERROR_LENGTH 1 -#define DM_SBCS_SBBUSYERROR 0x400000 +#define DM_SBCS_SBBUSYERROR_OFFSET 0x16ULL +#define DM_SBCS_SBBUSYERROR_LENGTH 1ULL +#define DM_SBCS_SBBUSYERROR 0x400000ULL /* - * When 1, indicates the system bus master is busy. (Whether the + * When 1, indicates the system bus manager is busy. (Whether the * system bus itself is busy is related, but not the same thing.) This * bit goes high immediately when a read or write is requested for any * reason, and does not go low until the access is fully completed. * - * Writes to \RdmSbcs while \FdmSbcsSbbusy is high result in undefined - * behavior. A debugger must not write to \RdmSbcs until it reads - * \FdmSbcsSbbusy as 0. + * Writes to {dm-sbcs} while {sbcs-sbbusy} is high result in undefined + * behavior. A debugger must not write to {dm-sbcs} until it reads + * {sbcs-sbbusy} as 0. */ -#define DM_SBCS_SBBUSY_OFFSET 0x15 -#define DM_SBCS_SBBUSY_LENGTH 1 -#define DM_SBCS_SBBUSY 0x200000 +#define DM_SBCS_SBBUSY_OFFSET 0x15ULL +#define DM_SBCS_SBBUSY_LENGTH 1ULL +#define DM_SBCS_SBBUSY 0x200000ULL /* - * When 1, every write to \RdmSbaddressZero automatically triggers a + * When 1, every write to {dm-sbaddress0} automatically triggers a * system bus read at the new address. */ -#define DM_SBCS_SBREADONADDR_OFFSET 0x14 -#define DM_SBCS_SBREADONADDR_LENGTH 1 -#define DM_SBCS_SBREADONADDR 0x100000 +#define DM_SBCS_SBREADONADDR_OFFSET 0x14ULL +#define DM_SBCS_SBREADONADDR_LENGTH 1ULL +#define DM_SBCS_SBREADONADDR 0x100000ULL /* * Select the access size to use for system bus accesses. */ -#define DM_SBCS_SBACCESS_OFFSET 0x11 -#define DM_SBCS_SBACCESS_LENGTH 3 -#define DM_SBCS_SBACCESS 0xe0000 +#define DM_SBCS_SBACCESS_OFFSET 0x11ULL +#define DM_SBCS_SBACCESS_LENGTH 3ULL +#define DM_SBCS_SBACCESS 0xe0000ULL /* * 8bit: 8-bit */ @@ -2570,35 +2759,35 @@ */ #define DM_SBCS_SBACCESS_128BIT 4 /* - * If \FdmSbcsSbaccess has an unsupported value when the DM starts a bus - * access, the access is not performed and \FdmSbcsSberror is set to 4. + * If {sbcs-sbaccess} has an unsupported value when the DM starts a bus + * access, the access is not performed and {sbcs-sberror} is set to 4. */ /* - * When 1, {\tt sbaddress} is incremented by the access size (in - * bytes) selected in \FdmSbcsSbaccess after every system bus access. + * When 1, `sbaddress` is incremented by the access size (in + * bytes) selected in {sbcs-sbaccess} after every system bus access. */ -#define DM_SBCS_SBAUTOINCREMENT_OFFSET 0x10 -#define DM_SBCS_SBAUTOINCREMENT_LENGTH 1 -#define DM_SBCS_SBAUTOINCREMENT 0x10000 +#define DM_SBCS_SBAUTOINCREMENT_OFFSET 0x10ULL +#define DM_SBCS_SBAUTOINCREMENT_LENGTH 1ULL +#define DM_SBCS_SBAUTOINCREMENT 0x10000ULL /* - * When 1, every read from \RdmSbdataZero automatically triggers a + * When 1, every read from {dm-sbdata0} automatically triggers a * system bus read at the (possibly auto-incremented) address. */ -#define DM_SBCS_SBREADONDATA_OFFSET 0xf -#define DM_SBCS_SBREADONDATA_LENGTH 1 -#define DM_SBCS_SBREADONDATA 0x8000 +#define DM_SBCS_SBREADONDATA_OFFSET 0xfULL +#define DM_SBCS_SBREADONDATA_LENGTH 1ULL +#define DM_SBCS_SBREADONDATA 0x8000ULL /* * When the Debug Module's system bus - * master encounters an error, this field gets set. The bits in this + * manager encounters an error, this field gets set. The bits in this * field remain set until they are cleared by writing 1 to them. * While this field is non-zero, no more system bus accesses can be * initiated by the Debug Module. * * An implementation may report ``Other'' (7) for any error condition. */ -#define DM_SBCS_SBERROR_OFFSET 0xc -#define DM_SBCS_SBERROR_LENGTH 3 -#define DM_SBCS_SBERROR 0x7000 +#define DM_SBCS_SBERROR_OFFSET 0xcULL +#define DM_SBCS_SBERROR_LENGTH 3ULL +#define DM_SBCS_SBERROR 0x7000ULL /* * none: There was no bus error. */ @@ -2627,101 +2816,101 @@ * Width of system bus addresses in bits. (0 indicates there is no bus * access support.) */ -#define DM_SBCS_SBASIZE_OFFSET 5 -#define DM_SBCS_SBASIZE_LENGTH 7 -#define DM_SBCS_SBASIZE 0xfe0 +#define DM_SBCS_SBASIZE_OFFSET 5ULL +#define DM_SBCS_SBASIZE_LENGTH 7ULL +#define DM_SBCS_SBASIZE 0xfe0ULL /* * 1 when 128-bit system bus accesses are supported. */ -#define DM_SBCS_SBACCESS128_OFFSET 4 -#define DM_SBCS_SBACCESS128_LENGTH 1 -#define DM_SBCS_SBACCESS128 0x10 +#define DM_SBCS_SBACCESS128_OFFSET 4ULL +#define DM_SBCS_SBACCESS128_LENGTH 1ULL +#define DM_SBCS_SBACCESS128 0x10ULL /* * 1 when 64-bit system bus accesses are supported. */ -#define DM_SBCS_SBACCESS64_OFFSET 3 -#define DM_SBCS_SBACCESS64_LENGTH 1 -#define DM_SBCS_SBACCESS64 8 +#define DM_SBCS_SBACCESS64_OFFSET 3ULL +#define DM_SBCS_SBACCESS64_LENGTH 1ULL +#define DM_SBCS_SBACCESS64 8ULL /* * 1 when 32-bit system bus accesses are supported. */ -#define DM_SBCS_SBACCESS32_OFFSET 2 -#define DM_SBCS_SBACCESS32_LENGTH 1 -#define DM_SBCS_SBACCESS32 4 +#define DM_SBCS_SBACCESS32_OFFSET 2ULL +#define DM_SBCS_SBACCESS32_LENGTH 1ULL +#define DM_SBCS_SBACCESS32 4ULL /* * 1 when 16-bit system bus accesses are supported. */ -#define DM_SBCS_SBACCESS16_OFFSET 1 -#define DM_SBCS_SBACCESS16_LENGTH 1 -#define DM_SBCS_SBACCESS16 2 +#define DM_SBCS_SBACCESS16_OFFSET 1ULL +#define DM_SBCS_SBACCESS16_LENGTH 1ULL +#define DM_SBCS_SBACCESS16 2ULL /* * 1 when 8-bit system bus accesses are supported. */ -#define DM_SBCS_SBACCESS8_OFFSET 0 -#define DM_SBCS_SBACCESS8_LENGTH 1 -#define DM_SBCS_SBACCESS8 1 +#define DM_SBCS_SBACCESS8_OFFSET 0ULL +#define DM_SBCS_SBACCESS8_LENGTH 1ULL +#define DM_SBCS_SBACCESS8 1ULL #define DM_SBADDRESS0 0x39 /* - * Accesses bits 31:0 of the physical address in {\tt sbaddress}. + * Accesses bits 31:0 of the physical address in `sbaddress`. */ -#define DM_SBADDRESS0_ADDRESS_OFFSET 0 -#define DM_SBADDRESS0_ADDRESS_LENGTH 0x20 -#define DM_SBADDRESS0_ADDRESS 0xffffffffU +#define DM_SBADDRESS0_ADDRESS_OFFSET 0ULL +#define DM_SBADDRESS0_ADDRESS_LENGTH 0x20ULL +#define DM_SBADDRESS0_ADDRESS 0xffffffffULL #define DM_SBADDRESS1 0x3a /* - * Accesses bits 63:32 of the physical address in {\tt sbaddress} (if + * Accesses bits 63:32 of the physical address in `sbaddress` (if * the system address bus is that wide). */ -#define DM_SBADDRESS1_ADDRESS_OFFSET 0 -#define DM_SBADDRESS1_ADDRESS_LENGTH 0x20 -#define DM_SBADDRESS1_ADDRESS 0xffffffffU +#define DM_SBADDRESS1_ADDRESS_OFFSET 0ULL +#define DM_SBADDRESS1_ADDRESS_LENGTH 0x20ULL +#define DM_SBADDRESS1_ADDRESS 0xffffffffULL #define DM_SBADDRESS2 0x3b /* - * Accesses bits 95:64 of the physical address in {\tt sbaddress} (if + * Accesses bits 95:64 of the physical address in `sbaddress` (if * the system address bus is that wide). */ -#define DM_SBADDRESS2_ADDRESS_OFFSET 0 -#define DM_SBADDRESS2_ADDRESS_LENGTH 0x20 -#define DM_SBADDRESS2_ADDRESS 0xffffffffU +#define DM_SBADDRESS2_ADDRESS_OFFSET 0ULL +#define DM_SBADDRESS2_ADDRESS_LENGTH 0x20ULL +#define DM_SBADDRESS2_ADDRESS 0xffffffffULL #define DM_SBADDRESS3 0x37 /* - * Accesses bits 127:96 of the physical address in {\tt sbaddress} (if + * Accesses bits 127:96 of the physical address in `sbaddress` (if * the system address bus is that wide). */ -#define DM_SBADDRESS3_ADDRESS_OFFSET 0 -#define DM_SBADDRESS3_ADDRESS_LENGTH 0x20 -#define DM_SBADDRESS3_ADDRESS 0xffffffffU +#define DM_SBADDRESS3_ADDRESS_OFFSET 0ULL +#define DM_SBADDRESS3_ADDRESS_LENGTH 0x20ULL +#define DM_SBADDRESS3_ADDRESS 0xffffffffULL #define DM_SBDATA0 0x3c /* - * Accesses bits 31:0 of {\tt sbdata}. + * Accesses bits 31:0 of `sbdata`. */ -#define DM_SBDATA0_DATA_OFFSET 0 -#define DM_SBDATA0_DATA_LENGTH 0x20 -#define DM_SBDATA0_DATA 0xffffffffU +#define DM_SBDATA0_DATA_OFFSET 0ULL +#define DM_SBDATA0_DATA_LENGTH 0x20ULL +#define DM_SBDATA0_DATA 0xffffffffULL #define DM_SBDATA1 0x3d /* - * Accesses bits 63:32 of {\tt sbdata} (if the system bus is that + * Accesses bits 63:32 of `sbdata` (if the system bus is that * wide). */ -#define DM_SBDATA1_DATA_OFFSET 0 -#define DM_SBDATA1_DATA_LENGTH 0x20 -#define DM_SBDATA1_DATA 0xffffffffU +#define DM_SBDATA1_DATA_OFFSET 0ULL +#define DM_SBDATA1_DATA_LENGTH 0x20ULL +#define DM_SBDATA1_DATA 0xffffffffULL #define DM_SBDATA2 0x3e /* - * Accesses bits 95:64 of {\tt sbdata} (if the system bus is that + * Accesses bits 95:64 of `sbdata` (if the system bus is that * wide). */ -#define DM_SBDATA2_DATA_OFFSET 0 -#define DM_SBDATA2_DATA_LENGTH 0x20 -#define DM_SBDATA2_DATA 0xffffffffU +#define DM_SBDATA2_DATA_OFFSET 0ULL +#define DM_SBDATA2_DATA_LENGTH 0x20ULL +#define DM_SBDATA2_DATA 0xffffffffULL #define DM_SBDATA3 0x3f /* - * Accesses bits 127:96 of {\tt sbdata} (if the system bus is that + * Accesses bits 127:96 of `sbdata` (if the system bus is that * wide). */ -#define DM_SBDATA3_DATA_OFFSET 0 -#define DM_SBDATA3_DATA_LENGTH 0x20 -#define DM_SBDATA3_DATA 0xffffffffU +#define DM_SBDATA3_DATA_OFFSET 0ULL +#define DM_SBDATA3_DATA_LENGTH 0x20ULL +#define DM_SBDATA3_DATA 0xffffffffULL #define DM_CUSTOM 0x1f #define DM_CUSTOM0 0x70 #define DM_CUSTOM1 0x71 @@ -2743,18 +2932,18 @@ /* * Description of what this field is used for. */ -#define SHORTNAME_FIELD_OFFSET 0 -#define SHORTNAME_FIELD_LENGTH 8 -#define SHORTNAME_FIELD 0xff +#define SHORTNAME_FIELD_OFFSET 0ULL +#define SHORTNAME_FIELD_LENGTH 8ULL +#define SHORTNAME_FIELD 0xffULL /* * This is 0 to indicate Access Register Command. */ -#define AC_ACCESS_REGISTER_CMDTYPE_OFFSET 0x18 -#define AC_ACCESS_REGISTER_CMDTYPE_LENGTH 8 -#define AC_ACCESS_REGISTER_CMDTYPE 0xff000000U -#define AC_ACCESS_REGISTER_AARSIZE_OFFSET 0x14 -#define AC_ACCESS_REGISTER_AARSIZE_LENGTH 3 -#define AC_ACCESS_REGISTER_AARSIZE 0x700000 +#define AC_ACCESS_REGISTER_CMDTYPE_OFFSET 0x18ULL +#define AC_ACCESS_REGISTER_CMDTYPE_LENGTH 8ULL +#define AC_ACCESS_REGISTER_CMDTYPE 0xff000000ULL +#define AC_ACCESS_REGISTER_AARSIZE_OFFSET 0x14ULL +#define AC_ACCESS_REGISTER_AARSIZE_LENGTH 3ULL +#define AC_ACCESS_REGISTER_AARSIZE 0x700000ULL /* * 32bit: Access the lowest 32 bits of the register. */ @@ -2768,36 +2957,36 @@ */ #define AC_ACCESS_REGISTER_AARSIZE_128BIT 4 /* - * If \FacAccessregisterAarsize specifies a size larger than the register's actual size, - * then the access must fail. If a register is accessible, then reads of \FacAccessregisterAarsize + * If {accessregister-aarsize} specifies a size larger than the register's actual size, + * then the access must fail. If a register is accessible, then reads of {accessregister-aarsize} * less than or equal to the register's actual size must be supported. * Writing less than the full register may be supported, but what - * happens to the high bits in that case is \unspecified. + * happens to the high bits in that case is UNSPECIFIED. * * This field controls the Argument Width as referenced in - * Table~\ref{tab:datareg}. + * xref:tab:datareg[]. */ -#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_OFFSET 0x13 -#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_LENGTH 1 -#define AC_ACCESS_REGISTER_AARPOSTINCREMENT 0x80000 +#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_OFFSET 0x13ULL +#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_LENGTH 1ULL +#define AC_ACCESS_REGISTER_AARPOSTINCREMENT 0x80000ULL /* * disabled: No effect. This variant must be supported. */ #define AC_ACCESS_REGISTER_AARPOSTINCREMENT_DISABLED 0 /* - * enabled: After a successful register access, \FacAccessregisterRegno is + * enabled: After a successful register access, {accessregister-regno} is * incremented. Incrementing past the highest supported value - * causes \FacAccessregisterRegno to become \unspecified. Supporting + * causes {accessregister-regno} to become UNSPECIFIED. Supporting * this variant is optional. It is undefined whether the increment - * happens when \FacAccessregisterTransfer is 0. + * happens when {accessregister-transfer} is 0. */ #define AC_ACCESS_REGISTER_AARPOSTINCREMENT_ENABLED 1 -#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 0x12 -#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH 1 -#define AC_ACCESS_REGISTER_POSTEXEC 0x40000 +#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 0x12ULL +#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH 1ULL +#define AC_ACCESS_REGISTER_POSTEXEC 0x40000ULL /* * disabled: No effect. This variant must be supported, and is the only - * supported one if \FdmAbstractcsProgbufsize is 0. + * supported one if {abstractcs-progbufsize} is 0. */ #define AC_ACCESS_REGISTER_POSTEXEC_DISABLED 0 /* @@ -2806,83 +2995,83 @@ * optional. */ #define AC_ACCESS_REGISTER_POSTEXEC_ENABLED 1 -#define AC_ACCESS_REGISTER_TRANSFER_OFFSET 0x11 -#define AC_ACCESS_REGISTER_TRANSFER_LENGTH 1 -#define AC_ACCESS_REGISTER_TRANSFER 0x20000 +#define AC_ACCESS_REGISTER_TRANSFER_OFFSET 0x11ULL +#define AC_ACCESS_REGISTER_TRANSFER_LENGTH 1ULL +#define AC_ACCESS_REGISTER_TRANSFER 0x20000ULL /* - * disabled: Don't do the operation specified by \FacAccessregisterWrite. + * disabled: Don't do the operation specified by {accessregister-write}. */ #define AC_ACCESS_REGISTER_TRANSFER_DISABLED 0 /* - * enabled: Do the operation specified by \FacAccessregisterWrite. + * enabled: Do the operation specified by {accessregister-write}. */ #define AC_ACCESS_REGISTER_TRANSFER_ENABLED 1 /* * This bit can be used to just execute the Program Buffer without - * having to worry about placing valid values into \FacAccessregisterAarsize or \FacAccessregisterRegno. + * having to worry about placing valid values into {accessregister-aarsize} or {accessregister-regno}. */ /* - * When \FacAccessregisterTransfer is set: + * When {accessregister-transfer} is set: */ -#define AC_ACCESS_REGISTER_WRITE_OFFSET 0x10 -#define AC_ACCESS_REGISTER_WRITE_LENGTH 1 -#define AC_ACCESS_REGISTER_WRITE 0x10000 +#define AC_ACCESS_REGISTER_WRITE_OFFSET 0x10ULL +#define AC_ACCESS_REGISTER_WRITE_LENGTH 1ULL +#define AC_ACCESS_REGISTER_WRITE 0x10000ULL /* - * arg0: Copy data from the specified register into {\tt arg0} portion - * of {\tt data}. + * arg0: Copy data from the specified register into `arg0` portion + * of `data`. */ #define AC_ACCESS_REGISTER_WRITE_ARG0 0 /* - * register: Copy data from {\tt arg0} portion of {\tt data} into the + * register: Copy data from `arg0` portion of `data` into the * specified register. */ #define AC_ACCESS_REGISTER_WRITE_REGISTER 1 /* * Number of the register to access, as described in - * Table~\ref{tab:regno}. - * \RcsrDpc may be used as an alias for PC if this command is + * xref:tab:regno[]. + * {csr-dpc} may be used as an alias for PC if this command is * supported on a non-halted hart. */ -#define AC_ACCESS_REGISTER_REGNO_OFFSET 0 -#define AC_ACCESS_REGISTER_REGNO_LENGTH 0x10 -#define AC_ACCESS_REGISTER_REGNO 0xffff +#define AC_ACCESS_REGISTER_REGNO_OFFSET 0ULL +#define AC_ACCESS_REGISTER_REGNO_LENGTH 0x10ULL +#define AC_ACCESS_REGISTER_REGNO 0xffffULL /* * This is 1 to indicate Quick Access command. */ -#define AC_QUICK_ACCESS_CMDTYPE_OFFSET 0x18 -#define AC_QUICK_ACCESS_CMDTYPE_LENGTH 8 -#define AC_QUICK_ACCESS_CMDTYPE 0xff000000U +#define AC_QUICK_ACCESS_CMDTYPE_OFFSET 0x18ULL +#define AC_QUICK_ACCESS_CMDTYPE_LENGTH 8ULL +#define AC_QUICK_ACCESS_CMDTYPE 0xff000000ULL /* * This is 2 to indicate Access Memory Command. */ -#define AC_ACCESS_MEMORY_CMDTYPE_OFFSET 0x18 -#define AC_ACCESS_MEMORY_CMDTYPE_LENGTH 8 -#define AC_ACCESS_MEMORY_CMDTYPE 0xff000000U +#define AC_ACCESS_MEMORY_CMDTYPE_OFFSET 0x18ULL +#define AC_ACCESS_MEMORY_CMDTYPE_LENGTH 8ULL +#define AC_ACCESS_MEMORY_CMDTYPE 0xff000000ULL /* * An implementation does not have to implement both virtual and * physical accesses, but it must fail accesses that it doesn't * support. */ -#define AC_ACCESS_MEMORY_AAMVIRTUAL_OFFSET 0x17 -#define AC_ACCESS_MEMORY_AAMVIRTUAL_LENGTH 1 -#define AC_ACCESS_MEMORY_AAMVIRTUAL 0x800000 +#define AC_ACCESS_MEMORY_AAMVIRTUAL_OFFSET 0x17ULL +#define AC_ACCESS_MEMORY_AAMVIRTUAL_LENGTH 1ULL +#define AC_ACCESS_MEMORY_AAMVIRTUAL 0x800000ULL /* * physical: Addresses are physical (to the hart they are performed on). */ #define AC_ACCESS_MEMORY_AAMVIRTUAL_PHYSICAL 0 /* * virtual: Addresses are virtual, and translated the way they would be from - * M-mode, with \FcsrMstatusMprv set. + * M-mode, with `MPRV` set. */ #define AC_ACCESS_MEMORY_AAMVIRTUAL_VIRTUAL 1 /* * Debug Modules on systems without address translation (i.e. virtual addresses equal physical) - * may optionally allow \FacAccessmemoryAamvirtual set to 1, which would produce the same result as - * that same abstract command with \FacAccessmemoryAamvirtual cleared. + * may optionally allow {accessmemory-aamvirtual} set to 1, which would produce the same result as + * that same abstract command with {accessmemory-aamvirtual} cleared. */ -#define AC_ACCESS_MEMORY_AAMSIZE_OFFSET 0x14 -#define AC_ACCESS_MEMORY_AAMSIZE_LENGTH 3 -#define AC_ACCESS_MEMORY_AAMSIZE 0x700000 +#define AC_ACCESS_MEMORY_AAMSIZE_OFFSET 0x14ULL +#define AC_ACCESS_MEMORY_AAMSIZE_LENGTH 3ULL +#define AC_ACCESS_MEMORY_AAMSIZE 0x700000ULL /* * 8bit: Access the lowest 8 bits of the memory location. */ @@ -2905,157 +3094,141 @@ #define AC_ACCESS_MEMORY_AAMSIZE_128BIT 4 /* * After a memory access has completed, if this bit is 1, increment - * {\tt arg1} (which contains the address used) by the number of bytes - * encoded in \FacAccessmemoryAamsize. + * `arg1` (which contains the address used) by the number of bytes + * encoded in {accessmemory-aamsize}. * * Supporting this variant is optional, but highly recommended for * performance reasons. */ -#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_OFFSET 0x13 -#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_LENGTH 1 -#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT 0x80000 -#define AC_ACCESS_MEMORY_WRITE_OFFSET 0x10 -#define AC_ACCESS_MEMORY_WRITE_LENGTH 1 -#define AC_ACCESS_MEMORY_WRITE 0x10000 +#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_OFFSET 0x13ULL +#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_LENGTH 1ULL +#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT 0x80000ULL +#define AC_ACCESS_MEMORY_WRITE_OFFSET 0x10ULL +#define AC_ACCESS_MEMORY_WRITE_LENGTH 1ULL +#define AC_ACCESS_MEMORY_WRITE 0x10000ULL /* - * arg0: Copy data from the memory location specified in {\tt arg1} into - * the low bits of {\tt arg0}. The value of the remaining bits of - * {\tt arg0} are \unspecified. + * arg0: Copy data from the memory location specified in `arg1` into + * the low bits of `arg0`. The value of the remaining bits of + * `arg0` are UNSPECIFIED. */ #define AC_ACCESS_MEMORY_WRITE_ARG0 0 /* - * memory: Copy data from the low bits of {\tt arg0} into the memory - * location specified in {\tt arg1}. + * memory: Copy data from the low bits of `arg0` into the memory + * location specified in `arg1`. */ #define AC_ACCESS_MEMORY_WRITE_MEMORY 1 /* * These bits are reserved for target-specific uses. */ -#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_OFFSET 0xe -#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_LENGTH 2 -#define AC_ACCESS_MEMORY_TARGET_SPECIFIC 0xc000 +#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_OFFSET 0xeULL +#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_LENGTH 2ULL +#define AC_ACCESS_MEMORY_TARGET_SPECIFIC 0xc000ULL #define VIRT_PRIV virtual /* * Contains the virtualization mode the hart was operating in when Debug - * Mode was entered. The encoding is described in Table \ref{tab:privmode}, + * Mode was entered. The encoding is described in <>, * and matches the virtualization mode encoding from the Privileged Spec. * A user can write this value to change the hart's virtualization mode * when exiting Debug Mode. */ -#define VIRT_PRIV_V_OFFSET 2 -#define VIRT_PRIV_V_LENGTH 1 -#define VIRT_PRIV_V 4 +#define VIRT_PRIV_V_OFFSET 2ULL +#define VIRT_PRIV_V_LENGTH 1ULL +#define VIRT_PRIV_V 4ULL /* * Contains the privilege mode the hart was operating in when Debug - * Mode was entered. The encoding is described in Table - * \ref{tab:privmode}, and matches the privilege mode encoding from + * Mode was entered. The encoding is described in <>, and matches the privilege mode encoding from * the Privileged Spec. A user can write this * value to change the hart's privilege mode when exiting Debug Mode. */ -#define VIRT_PRIV_PRV_OFFSET 0 -#define VIRT_PRIV_PRV_LENGTH 2 -#define VIRT_PRIV_PRV 3 -#define DMI_SERCS 0x34 -/* - * Number of supported serial ports. - */ -#define DMI_SERCS_SERIALCOUNT_OFFSET 0x1c -#define DMI_SERCS_SERIALCOUNT_LENGTH 4 -#define DMI_SERCS_SERIALCOUNT 0xf0000000U -/* - * Select which serial port is accessed by \RdmiSerrx and \RdmiSertx. - */ -#define DMI_SERCS_SERIAL_OFFSET 0x18 -#define DMI_SERCS_SERIAL_LENGTH 3 -#define DMI_SERCS_SERIAL 0x7000000 -#define DMI_SERCS_ERROR7_OFFSET 0x17 -#define DMI_SERCS_ERROR7_LENGTH 1 -#define DMI_SERCS_ERROR7 0x800000 -#define DMI_SERCS_VALID7_OFFSET 0x16 -#define DMI_SERCS_VALID7_LENGTH 1 -#define DMI_SERCS_VALID7 0x400000 -#define DMI_SERCS_FULL7_OFFSET 0x15 -#define DMI_SERCS_FULL7_LENGTH 1 -#define DMI_SERCS_FULL7 0x200000 -#define DMI_SERCS_ERROR6_OFFSET 0x14 -#define DMI_SERCS_ERROR6_LENGTH 1 -#define DMI_SERCS_ERROR6 0x100000 -#define DMI_SERCS_VALID6_OFFSET 0x13 -#define DMI_SERCS_VALID6_LENGTH 1 -#define DMI_SERCS_VALID6 0x80000 -#define DMI_SERCS_FULL6_OFFSET 0x12 -#define DMI_SERCS_FULL6_LENGTH 1 -#define DMI_SERCS_FULL6 0x40000 -#define DMI_SERCS_ERROR5_OFFSET 0x11 -#define DMI_SERCS_ERROR5_LENGTH 1 -#define DMI_SERCS_ERROR5 0x20000 -#define DMI_SERCS_VALID5_OFFSET 0x10 -#define DMI_SERCS_VALID5_LENGTH 1 -#define DMI_SERCS_VALID5 0x10000 -#define DMI_SERCS_FULL5_OFFSET 0xf -#define DMI_SERCS_FULL5_LENGTH 1 -#define DMI_SERCS_FULL5 0x8000 -#define DMI_SERCS_ERROR4_OFFSET 0xe -#define DMI_SERCS_ERROR4_LENGTH 1 -#define DMI_SERCS_ERROR4 0x4000 -#define DMI_SERCS_VALID4_OFFSET 0xd -#define DMI_SERCS_VALID4_LENGTH 1 -#define DMI_SERCS_VALID4 0x2000 -#define DMI_SERCS_FULL4_OFFSET 0xc -#define DMI_SERCS_FULL4_LENGTH 1 -#define DMI_SERCS_FULL4 0x1000 -#define DMI_SERCS_ERROR3_OFFSET 0xb -#define DMI_SERCS_ERROR3_LENGTH 1 -#define DMI_SERCS_ERROR3 0x800 -#define DMI_SERCS_VALID3_OFFSET 0xa -#define DMI_SERCS_VALID3_LENGTH 1 -#define DMI_SERCS_VALID3 0x400 -#define DMI_SERCS_FULL3_OFFSET 9 -#define DMI_SERCS_FULL3_LENGTH 1 -#define DMI_SERCS_FULL3 0x200 -#define DMI_SERCS_ERROR2_OFFSET 8 -#define DMI_SERCS_ERROR2_LENGTH 1 -#define DMI_SERCS_ERROR2 0x100 -#define DMI_SERCS_VALID2_OFFSET 7 -#define DMI_SERCS_VALID2_LENGTH 1 -#define DMI_SERCS_VALID2 0x80 -#define DMI_SERCS_FULL2_OFFSET 6 -#define DMI_SERCS_FULL2_LENGTH 1 -#define DMI_SERCS_FULL2 0x40 -#define DMI_SERCS_ERROR1_OFFSET 5 -#define DMI_SERCS_ERROR1_LENGTH 1 -#define DMI_SERCS_ERROR1 0x20 -#define DMI_SERCS_VALID1_OFFSET 4 -#define DMI_SERCS_VALID1_LENGTH 1 -#define DMI_SERCS_VALID1 0x10 -#define DMI_SERCS_FULL1_OFFSET 3 -#define DMI_SERCS_FULL1_LENGTH 1 -#define DMI_SERCS_FULL1 8 -/* - * 1 when the debugger-to-core queue for serial port 0 has - * over or underflowed. This bit will remain set until it is reset by - * writing 1 to this bit. - */ -#define DMI_SERCS_ERROR0_OFFSET 2 -#define DMI_SERCS_ERROR0_LENGTH 1 -#define DMI_SERCS_ERROR0 4 -/* - * 1 when the core-to-debugger queue for serial port 0 is not empty. - */ -#define DMI_SERCS_VALID0_OFFSET 1 -#define DMI_SERCS_VALID0_LENGTH 1 -#define DMI_SERCS_VALID0 2 -/* - * 1 when the debugger-to-core queue for serial port 0 is full. - */ -#define DMI_SERCS_FULL0_OFFSET 0 -#define DMI_SERCS_FULL0_LENGTH 1 -#define DMI_SERCS_FULL0 1 -#define DMI_SERTX 0x35 -#define DMI_SERTX_DATA_OFFSET 0 -#define DMI_SERTX_DATA_LENGTH 0x20 -#define DMI_SERTX_DATA 0xffffffffU -#define DMI_SERRX 0x36 -#define DMI_SERRX_DATA_OFFSET 0 -#define DMI_SERRX_DATA_LENGTH 0x20 -#define DMI_SERRX_DATA 0xffffffffU +#define VIRT_PRIV_PRV_OFFSET 0ULL +#define VIRT_PRIV_PRV_LENGTH 2ULL +#define VIRT_PRIV_PRV 3ULL +enum riscv_debug_reg_ordinal { + DTM_IDCODE_ORDINAL, + DTM_DTMCS_ORDINAL, + DTM_DMI_ORDINAL, + DTM_BYPASS_ORDINAL, + CSR_DCSR_ORDINAL, + CSR_DPC_ORDINAL, + CSR_DSCRATCH0_ORDINAL, + CSR_DSCRATCH1_ORDINAL, + CSR_TSELECT_ORDINAL, + CSR_TDATA1_ORDINAL, + CSR_TDATA2_ORDINAL, + CSR_TDATA3_ORDINAL, + CSR_TINFO_ORDINAL, + CSR_TCONTROL_ORDINAL, + CSR_SCONTEXT_ORDINAL, + CSR_MCONTEXT_ORDINAL, + CSR_MCONTROL_ORDINAL, + CSR_MCONTROL6_ORDINAL, + CSR_ICOUNT_ORDINAL, + CSR_ITRIGGER_ORDINAL, + CSR_ETRIGGER_ORDINAL, + CSR_TMEXTTRIGGER_ORDINAL, + CSR_TEXTRA32_ORDINAL, + CSR_TEXTRA64_ORDINAL, + DM_DMSTATUS_ORDINAL, + DM_DMCONTROL_ORDINAL, + DM_HARTINFO_ORDINAL, + DM_HAWINDOWSEL_ORDINAL, + DM_HAWINDOW_ORDINAL, + DM_ABSTRACTCS_ORDINAL, + DM_COMMAND_ORDINAL, + DM_ABSTRACTAUTO_ORDINAL, + DM_CONFSTRPTR0_ORDINAL, + DM_CONFSTRPTR1_ORDINAL, + DM_CONFSTRPTR2_ORDINAL, + DM_CONFSTRPTR3_ORDINAL, + DM_NEXTDM_ORDINAL, + DM_DATA0_ORDINAL, + DM_PROGBUF0_ORDINAL, + DM_AUTHDATA_ORDINAL, + DM_DMCS2_ORDINAL, + DM_HALTSUM0_ORDINAL, + DM_HALTSUM1_ORDINAL, + DM_HALTSUM2_ORDINAL, + DM_HALTSUM3_ORDINAL, + DM_SBCS_ORDINAL, + DM_SBADDRESS0_ORDINAL, + DM_SBADDRESS1_ORDINAL, + DM_SBADDRESS2_ORDINAL, + DM_SBADDRESS3_ORDINAL, + DM_SBDATA0_ORDINAL, + DM_SBDATA1_ORDINAL, + DM_SBDATA2_ORDINAL, + DM_SBDATA3_ORDINAL, + SHORTNAME_ORDINAL, + AC_ACCESS_REGISTER_ORDINAL, + AC_QUICK_ACCESS_ORDINAL, + AC_ACCESS_MEMORY_ORDINAL, + VIRT_PRIV_ORDINAL +}; +typedef struct { + struct { + unsigned int value; int is_set; + } DXLEN; + struct { + unsigned int value; int is_set; + } XLEN; + struct { + unsigned int value; int is_set; + } abits; +} riscv_debug_reg_ctx_t; + +typedef struct { + const char *name; + unsigned int lsb; // inclusive + unsigned int msb; // inclusive + const char **values; // If non-NULL, array of human-readable string for each possible value +} riscv_debug_reg_field_info_t; +typedef struct riscv_debug_reg_field_list_t { + riscv_debug_reg_field_info_t field; + struct riscv_debug_reg_field_list_t (*get_next)(riscv_debug_reg_ctx_t context); +} riscv_debug_reg_field_list_t; +typedef struct { + const char *name; + struct riscv_debug_reg_field_list_t (* const get_fields_head)(riscv_debug_reg_ctx_t context); +} riscv_debug_reg_info_t; +riscv_debug_reg_info_t get_riscv_debug_reg_info(enum riscv_debug_reg_ordinal reg_ordinal); +#endif diff --git a/src/target/riscv/debug_reg_printer.c b/src/target/riscv/debug_reg_printer.c new file mode 100644 index 000000000..fc7dabd26 --- /dev/null +++ b/src/target/riscv/debug_reg_printer.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "debug_reg_printer.h" + +static unsigned int get_len_or_sprintf(char *buf, unsigned int curr, const char *format, ...) +{ + assert(format); + va_list args; + int length; + + va_start(args, format); + if (buf) + length = vsprintf(buf + curr, format, args); + else + length = vsnprintf(NULL, 0, format, args); + va_end(args); + assert(length >= 0); + return (unsigned int)length; +} + +static unsigned int print_number(char *buf, unsigned int offset, uint64_t value) +{ + const char * const format = value > 9 ? "0x%" PRIx64 : "%" PRIx64; + + return get_len_or_sprintf(buf, offset, format, value); +} + +static unsigned int riscv_debug_reg_field_value_to_s(char *buf, unsigned int offset, + const char * const *field_value_names, uint64_t field_value) +{ + const char * const field_value_name = field_value_names ? + field_value_names[field_value] : + NULL; + + if (!field_value_name) + return print_number(buf, offset, field_value); + return get_len_or_sprintf(buf, offset, "%s", field_value_name); +} + +static unsigned int riscv_debug_reg_field_to_s(char *buf, unsigned int offset, + riscv_debug_reg_field_info_t field, riscv_debug_reg_ctx_t context, + uint64_t field_value) +{ + const unsigned int name_len = get_len_or_sprintf(buf, offset, "%s=", field.name); + + return name_len + riscv_debug_reg_field_value_to_s(buf, offset + name_len, + field.values, field_value); +} + +static uint64_t riscv_debug_reg_field_value(riscv_debug_reg_field_info_t field, uint64_t value) +{ + assert(field.msb < 64); + assert(field.msb >= field.lsb); + const uint64_t trailing_ones_mask = (uint64_t)(-1) >> (63 - field.msb); + return (value & trailing_ones_mask) >> field.lsb; +} + +static unsigned int riscv_debug_reg_fields_to_s(char *buf, unsigned int offset, + struct riscv_debug_reg_field_list_t (*get_next)(riscv_debug_reg_ctx_t contex), + riscv_debug_reg_ctx_t context, uint64_t value, + enum riscv_debug_reg_show show) +{ + unsigned int curr = offset; + curr += get_len_or_sprintf(buf, curr, " {"); + char *separator = ""; + for (struct riscv_debug_reg_field_list_t list; get_next; get_next = list.get_next) { + list = get_next(context); + + uint64_t field_value = riscv_debug_reg_field_value(list.field, value); + + if (show == RISCV_DEBUG_REG_SHOW_ALL || + (show == RISCV_DEBUG_REG_HIDE_UNNAMED_0 && + (field_value != 0 || + (list.field.values && list.field.values[0]))) || + (show == RISCV_DEBUG_REG_HIDE_ALL_0 && field_value != 0)) { + curr += get_len_or_sprintf(buf, curr, separator); + curr += riscv_debug_reg_field_to_s(buf, curr, list.field, context, + field_value); + separator = " "; + } + } + curr += get_len_or_sprintf(buf, curr, "}"); + return curr - offset; +} + +unsigned int riscv_debug_reg_to_s(char *buf, enum riscv_debug_reg_ordinal reg_ordinal, + riscv_debug_reg_ctx_t context, uint64_t value, + enum riscv_debug_reg_show show) +{ + unsigned int length = 0; + + riscv_debug_reg_info_t reg = get_riscv_debug_reg_info(reg_ordinal); + + length += get_len_or_sprintf(buf, length, "%s=", reg.name); + length += print_number(buf, length, value); + + if (reg.get_fields_head) + length += riscv_debug_reg_fields_to_s(buf, length, + reg.get_fields_head, context, value, show); + + if (buf) + buf[length] = '\0'; + return length; +} diff --git a/src/target/riscv/debug_reg_printer.h b/src/target/riscv/debug_reg_printer.h new file mode 100644 index 000000000..5089ff8fa --- /dev/null +++ b/src/target/riscv/debug_reg_printer.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_DEBUG_REG_PRINTER_H +#define OPENOCD_TARGET_RISCV_DEBUG_REG_PRINTER_H + +#include "debug_defines.h" + +enum riscv_debug_reg_show { + RISCV_DEBUG_REG_SHOW_ALL, + RISCV_DEBUG_REG_HIDE_ALL_0, + RISCV_DEBUG_REG_HIDE_UNNAMED_0, +}; + +/** + * This function is used to fill a buffer with a decoded string representation + * of register's value. + * @param buf If non-NULL, the buffer to write the string into. + * Otherwise, the function does not perform any writes, + * it just calculates the number of characters used. + * @param reg_ordinal + * The ordinal of the register. + * @param context The structure, containing the information about the target, + * necessary to decode the register. + * @param value The value to be decoded. + * + * Returns the number of characters used by the string representation + * (excluding '\0'). + * + * Example: + * const struct riscv_debug_reg_ctx_t context = { + * .abits = { .value = , .is_set = true } + * }; + * char buf[riscv_debug_reg_to_s(NULL, DTM_DMI_ORDINAL, context, ) + 1] + * riscv_debug_reg_to_s(buf, DTM_DMI_ORDINAL, context, ); + */ +unsigned int riscv_debug_reg_to_s(char *buf, enum riscv_debug_reg_ordinal reg_ordinal, + riscv_debug_reg_ctx_t context, uint64_t value, + enum riscv_debug_reg_show show); + +#endif /* OPENOCD_TARGET_RISCV_DEBUG_REG_PRINTER_H */ diff --git a/src/target/riscv/encoding.h b/src/target/riscv/encoding.h index c2da4e676..3ac537c37 100644 --- a/src/target/riscv/encoding.h +++ b/src/target/riscv/encoding.h @@ -1,10 +1,10 @@ /* SPDX-License-Identifier: BSD-3-Clause */ -/* Copyright (c) 2022 RISC-V International */ +/* Copyright (c) 2023 RISC-V International */ /* * This file is auto-generated by running 'make' in - * https://github.com/riscv/riscv-opcodes (dcdf8d3) + * https://github.com/riscv/riscv-opcodes (ed68c21) */ #ifndef RISCV_CSR_ENCODING_H @@ -156,14 +156,17 @@ #define MENVCFG_CBIE 0x00000030 #define MENVCFG_CBCFE 0x00000040 #define MENVCFG_CBZE 0x00000080 +#define MENVCFG_HADE 0x2000000000000000 #define MENVCFG_PBMTE 0x4000000000000000 #define MENVCFG_STCE 0x8000000000000000 +#define MENVCFGH_HADE 0x20000000 #define MENVCFGH_PBMTE 0x40000000 #define MENVCFGH_STCE 0x80000000 #define MSTATEEN0_CS 0x00000001 #define MSTATEEN0_FCSR 0x00000002 +#define MSTATEEN0_JVT 0x00000004 #define MSTATEEN0_HCONTEXT 0x0200000000000000 #define MSTATEEN0_HENVCFG 0x4000000000000000 #define MSTATEEN_HSTATEEN 0x8000000000000000 @@ -190,14 +193,17 @@ #define HENVCFG_CBIE 0x00000030 #define HENVCFG_CBCFE 0x00000040 #define HENVCFG_CBZE 0x00000080 +#define HENVCFG_HADE 0x2000000000000000 #define HENVCFG_PBMTE 0x4000000000000000 #define HENVCFG_STCE 0x8000000000000000 +#define HENVCFGH_HADE 0x20000000 #define HENVCFGH_PBMTE 0x40000000 #define HENVCFGH_STCE 0x80000000 #define HSTATEEN0_CS 0x00000001 #define HSTATEEN0_FCSR 0x00000002 +#define HSTATEEN0_JVT 0x00000004 #define HSTATEEN0_SCONTEXT 0x0200000000000000 #define HSTATEEN0_SENVCFG 0x4000000000000000 #define HSTATEEN_SSTATEEN 0x8000000000000000 @@ -213,6 +219,7 @@ #define SSTATEEN0_CS 0x00000001 #define SSTATEEN0_FCSR 0x00000002 +#define SSTATEEN0_JVT 0x00000004 #define MSECCFG_MML 0x00000001 #define MSECCFG_MMWP 0x00000002 @@ -220,6 +227,10 @@ #define MSECCFG_USEED 0x00000100 #define MSECCFG_SSEED 0x00000200 +/* jvt fields */ +#define JVT_MODE 0x3F +#define JVT_BASE (~0x3F) + #define PRV_U 0 #define PRV_S 1 #define PRV_M 3 @@ -366,12 +377,8 @@ #define MASK_ADD8 0xfe00707f #define MATCH_ADD_UW 0x800003b #define MASK_ADD_UW 0xfe00707f -#define MATCH_ADDD 0x7b -#define MASK_ADDD 0xfe00707f #define MATCH_ADDI 0x13 #define MASK_ADDI 0x707f -#define MATCH_ADDID 0x5b -#define MASK_ADDID 0x707f #define MATCH_ADDIW 0x1b #define MASK_ADDIW 0x707f #define MATCH_ADDW 0x3b @@ -474,10 +481,6 @@ #define MASK_BINV 0xfe00707f #define MATCH_BINVI 0x68001013 #define MASK_BINVI 0xfc00707f -#define MATCH_BITREV 0xe6000077 -#define MASK_BITREV 0xfe00707f -#define MATCH_BITREVI 0xe8000077 -#define MASK_BITREVI 0xfc00707f #define MATCH_BLT 0x4063 #define MASK_BLT 0x707f #define MATCH_BLTU 0x6063 @@ -490,8 +493,6 @@ #define MASK_BMATXOR 0xfe00707f #define MATCH_BNE 0x1063 #define MASK_BNE 0x707f -#define MATCH_BPICK 0x3077 -#define MASK_BPICK 0x600707f #define MATCH_BSET 0x28001033 #define MASK_BSET 0xfe00707f #define MATCH_BSETI 0x28001013 @@ -542,38 +543,48 @@ #define MASK_C_JALR 0xf07f #define MATCH_C_JR 0x8002 #define MASK_C_JR 0xf07f +#define MATCH_C_LBU 0x8000 +#define MASK_C_LBU 0xfc03 #define MATCH_C_LD 0x6000 #define MASK_C_LD 0xe003 #define MATCH_C_LDSP 0x6002 #define MASK_C_LDSP 0xe003 +#define MATCH_C_LH 0x8440 +#define MASK_C_LH 0xfc43 +#define MATCH_C_LHU 0x8400 +#define MASK_C_LHU 0xfc43 #define MATCH_C_LI 0x4001 #define MASK_C_LI 0xe003 -#define MATCH_C_LQ 0x2000 -#define MASK_C_LQ 0xe003 -#define MATCH_C_LQSP 0x2002 -#define MASK_C_LQSP 0xe003 #define MATCH_C_LUI 0x6001 #define MASK_C_LUI 0xe003 #define MATCH_C_LW 0x4000 #define MASK_C_LW 0xe003 #define MATCH_C_LWSP 0x4002 #define MASK_C_LWSP 0xe003 +#define MATCH_C_MUL 0x9c41 +#define MASK_C_MUL 0xfc63 #define MATCH_C_MV 0x8002 #define MASK_C_MV 0xf003 #define MATCH_C_NOP 0x1 #define MASK_C_NOP 0xef83 +#define MATCH_C_NOT 0x9c75 +#define MASK_C_NOT 0xfc7f #define MATCH_C_OR 0x8c41 #define MASK_C_OR 0xfc63 +#define MATCH_C_SB 0x8800 +#define MASK_C_SB 0xfc03 #define MATCH_C_SD 0xe000 #define MASK_C_SD 0xe003 #define MATCH_C_SDSP 0xe002 #define MASK_C_SDSP 0xe003 +#define MATCH_C_SEXT_B 0x9c65 +#define MASK_C_SEXT_B 0xfc7f +#define MATCH_C_SEXT_H 0x9c6d +#define MASK_C_SEXT_H 0xfc7f +#define MATCH_C_SH 0x8c00 +#define MASK_C_SH 0xfc43 #define MATCH_C_SLLI 0x2 #define MASK_C_SLLI 0xe003 -#define MATCH_C_SQ 0xa000 -#define MASK_C_SQ 0xe003 -#define MATCH_C_SQSP 0xa002 -#define MASK_C_SQSP 0xe003 #define MATCH_C_SRAI 0x8401 #define MASK_C_SRAI 0xec03 #define MATCH_C_SRLI 0x8001 @@ -588,6 +599,12 @@ #define MASK_C_SWSP 0xe003 #define MATCH_C_XOR 0x8c21 #define MASK_C_XOR 0xfc63 +#define MATCH_C_ZEXT_B 0x9c61 +#define MASK_C_ZEXT_B 0xfc7f +#define MATCH_C_ZEXT_H 0x9c69 +#define MASK_C_ZEXT_H 0xfc7f +#define MATCH_C_ZEXT_W 0x9c71 +#define MASK_C_ZEXT_W 0xfc7f #define MATCH_CBO_CLEAN 0x10200f #define MASK_CBO_CLEAN 0xfff07fff #define MATCH_CBO_FLUSH 0x20200f @@ -602,12 +619,6 @@ #define MASK_CLMULH 0xfe00707f #define MATCH_CLMULR 0xa002033 #define MASK_CLMULR 0xfe00707f -#define MATCH_CLO16 0xaeb00077 -#define MASK_CLO16 0xfff0707f -#define MATCH_CLO32 0xafb00077 -#define MASK_CLO32 0xfff0707f -#define MATCH_CLO8 0xae300077 -#define MASK_CLO8 0xfff0707f #define MATCH_CLRS16 0xae800077 #define MASK_CLRS16 0xfff0707f #define MATCH_CLRS32 0xaf800077 @@ -624,6 +635,20 @@ #define MASK_CLZ8 0xfff0707f #define MATCH_CLZW 0x6000101b #define MASK_CLZW 0xfff0707f +#define MATCH_CM_JALT 0xa002 +#define MASK_CM_JALT 0xfc03 +#define MATCH_CM_MVA01S 0xac62 +#define MASK_CM_MVA01S 0xfc63 +#define MATCH_CM_MVSA01 0xac22 +#define MASK_CM_MVSA01 0xfc63 +#define MATCH_CM_POP 0xba02 +#define MASK_CM_POP 0xff03 +#define MATCH_CM_POPRET 0xbe02 +#define MASK_CM_POPRET 0xff03 +#define MATCH_CM_POPRETZ 0xbc02 +#define MASK_CM_POPRETZ 0xff03 +#define MATCH_CM_PUSH 0xb802 +#define MASK_CM_PUSH 0xff03 #define MATCH_CMIX 0x6001033 #define MASK_CMIX 0x600707f #define MATCH_CMOV 0x6005033 @@ -676,6 +701,10 @@ #define MASK_CTZ 0xfff0707f #define MATCH_CTZW 0x6010101b #define MASK_CTZW 0xfff0707f +#define MATCH_CZERO_EQZ 0xe005033 +#define MASK_CZERO_EQZ 0xfe00707f +#define MATCH_CZERO_NEZ 0xe007033 +#define MASK_CZERO_NEZ 0xfe00707f #define MATCH_DIV 0x2004033 #define MASK_DIV 0xfe00707f #define MATCH_DIVU 0x2005033 @@ -1238,14 +1267,10 @@ #define MASK_LBU 0x707f #define MATCH_LD 0x3003 #define MASK_LD 0x707f -#define MATCH_LDU 0x7003 -#define MASK_LDU 0x707f #define MATCH_LH 0x1003 #define MASK_LH 0x707f #define MATCH_LHU 0x5003 #define MASK_LHU 0x707f -#define MATCH_LQ 0x300f -#define MASK_LQ 0x707f #define MATCH_LR_D 0x1000302f #define MASK_LR_D 0xf9f0707f #define MATCH_LR_W 0x1000202f @@ -1262,14 +1287,10 @@ #define MASK_MAX 0xfe00707f #define MATCH_MAXU 0xa007033 #define MASK_MAXU 0xfe00707f -#define MATCH_MAXW 0xf2000077 -#define MASK_MAXW 0xfe00707f #define MATCH_MIN 0xa004033 #define MASK_MIN 0xfe00707f #define MATCH_MINU 0xa005033 #define MASK_MINU 0xfe00707f -#define MATCH_MINW 0xf0000077 -#define MASK_MINW 0xfe00707f #define MATCH_MRET 0x30200073 #define MASK_MRET 0xffffffff #define MATCH_MSUBR32 0xc6001077 @@ -1312,8 +1333,6 @@ #define MASK_PBSADA 0xfe00707f #define MATCH_PKBB16 0xe001077 #define MASK_PKBB16 0xfe00707f -#define MATCH_PKBB32 0xe002077 -#define MASK_PKBB32 0xfe00707f #define MATCH_PKBT16 0x1e001077 #define MASK_PKBT16 0xfe00707f #define MATCH_PKBT32 0x1e002077 @@ -1324,8 +1343,6 @@ #define MASK_PKTB32 0xfe00707f #define MATCH_PKTT16 0x2e001077 #define MASK_PKTT16 0xfe00707f -#define MATCH_PKTT32 0x2e002077 -#define MASK_PKTT32 0xfe00707f #define MATCH_PREFETCH_I 0x6013 #define MASK_PREFETCH_I 0x1f07fff #define MATCH_PREFETCH_R 0x106013 @@ -1478,20 +1495,18 @@ #define MASK_SLL32 0xfe00707f #define MATCH_SLL8 0x5c000077 #define MASK_SLL8 0xfe00707f -#define MATCH_SLLD 0x107b -#define MASK_SLLD 0xfe00707f #define MATCH_SLLI 0x1013 -#define MASK_SLLI 0xf800707f +#define MASK_SLLI 0xfc00707f #define MATCH_SLLI16 0x74000077 #define MASK_SLLI16 0xff00707f #define MATCH_SLLI32 0x74002077 #define MASK_SLLI32 0xfe00707f #define MATCH_SLLI8 0x7c000077 #define MASK_SLLI8 0xff80707f +#define MATCH_SLLI_RV32 0x1013 +#define MASK_SLLI_RV32 0xfe00707f #define MATCH_SLLI_UW 0x800101b #define MASK_SLLI_UW 0xfc00707f -#define MATCH_SLLID 0x105b -#define MASK_SLLID 0xfc00707f #define MATCH_SLLIW 0x101b #define MASK_SLLIW 0xfe00707f #define MATCH_SLLW 0x103b @@ -1604,8 +1619,6 @@ #define MASK_SMXDS 0xfe00707f #define MATCH_SMXDS32 0x78002077 #define MASK_SMXDS32 0xfe00707f -#define MATCH_SQ 0x4023 -#define MASK_SQ 0x707f #define MATCH_SRA 0x40005033 #define MASK_SRA 0xfe00707f #define MATCH_SRA16 0x50000077 @@ -1622,10 +1635,8 @@ #define MASK_SRA8_U 0xfe00707f #define MATCH_SRA_U 0x24001077 #define MASK_SRA_U 0xfe00707f -#define MATCH_SRAD 0x4000507b -#define MASK_SRAD 0xfe00707f #define MATCH_SRAI 0x40005013 -#define MASK_SRAI 0xf800707f +#define MASK_SRAI 0xfc00707f #define MATCH_SRAI16 0x70000077 #define MASK_SRAI16 0xff00707f #define MATCH_SRAI16_U 0x71000077 @@ -1638,10 +1649,10 @@ #define MASK_SRAI8 0xff80707f #define MATCH_SRAI8_U 0x78800077 #define MASK_SRAI8_U 0xff80707f +#define MATCH_SRAI_RV32 0x40005013 +#define MASK_SRAI_RV32 0xfe00707f #define MATCH_SRAI_U 0xd4001077 #define MASK_SRAI_U 0xfc00707f -#define MATCH_SRAID 0x4000505b -#define MASK_SRAID 0xfc00707f #define MATCH_SRAIW 0x4000501b #define MASK_SRAIW 0xfe00707f #define MATCH_SRAIW_U 0x34001077 @@ -1664,10 +1675,8 @@ #define MASK_SRL8 0xfe00707f #define MATCH_SRL8_U 0x6a000077 #define MASK_SRL8_U 0xfe00707f -#define MATCH_SRLD 0x507b -#define MASK_SRLD 0xfe00707f #define MATCH_SRLI 0x5013 -#define MASK_SRLI 0xf800707f +#define MASK_SRLI 0xfc00707f #define MATCH_SRLI16 0x72000077 #define MASK_SRLI16 0xff00707f #define MATCH_SRLI16_U 0x73000077 @@ -1680,8 +1689,8 @@ #define MASK_SRLI8 0xff80707f #define MATCH_SRLI8_U 0x7a800077 #define MASK_SRLI8_U 0xff80707f -#define MATCH_SRLID 0x505b -#define MASK_SRLID 0xfc00707f +#define MATCH_SRLI_RV32 0x5013 +#define MASK_SRLI_RV32 0xfe00707f #define MATCH_SRLIW 0x501b #define MASK_SRLIW 0xfe00707f #define MATCH_SRLW 0x503b @@ -1712,8 +1721,6 @@ #define MASK_SUB64 0xfe00707f #define MATCH_SUB8 0x4a000077 #define MASK_SUB8 0xfe00707f -#define MATCH_SUBD 0x4000007b -#define MASK_SUBD 0xfe00707f #define MATCH_SUBW 0x4000003b #define MASK_SUBW 0xfe00707f #define MATCH_SUNPKD810 0xac800077 @@ -1728,8 +1735,6 @@ #define MASK_SUNPKD832 0xfff0707f #define MATCH_SW 0x2023 #define MASK_SW 0x707f -#define MATCH_SWAP8 0xad800077 -#define MASK_SWAP8 0xfff0707f #define MATCH_UCLIP16 0x85000077 #define MASK_UCLIP16 0xff00707f #define MATCH_UCLIP32 0xf4000077 @@ -2750,10 +2755,6 @@ #define MASK_VZEXT_VF4 0xfc0ff07f #define MATCH_VZEXT_VF8 0x48012057 #define MASK_VZEXT_VF8 0xfc0ff07f -#define MATCH_WEXT 0xce000077 -#define MASK_WEXT 0xfe00707f -#define MATCH_WEXTI 0xde000077 -#define MASK_WEXTI 0xfe00707f #define MATCH_WFI 0x10500073 #define MASK_WFI 0xffffffff #define MATCH_WRS_NTO 0xd00073 @@ -2793,6 +2794,7 @@ #define CSR_VXRM 0xa #define CSR_VCSR 0xf #define CSR_SEED 0x15 +#define CSR_JVT 0x17 #define CSR_CYCLE 0xc00 #define CSR_TIME 0xc01 #define CSR_INSTRET 0xc02 @@ -2845,6 +2847,9 @@ #define CSR_STVAL 0x143 #define CSR_SIP 0x144 #define CSR_STIMECMP 0x14d +#define CSR_SISELECT 0x150 +#define CSR_SIREG 0x151 +#define CSR_STOPEI 0x15c #define CSR_SATP 0x180 #define CSR_SCONTEXT 0x5a8 #define CSR_VSSTATUS 0x200 @@ -2856,6 +2861,9 @@ #define CSR_VSTVAL 0x243 #define CSR_VSIP 0x244 #define CSR_VSTIMECMP 0x24d +#define CSR_VSISELECT 0x250 +#define CSR_VSIREG 0x251 +#define CSR_VSTOPEI 0x25c #define CSR_VSATP 0x280 #define CSR_HSTATUS 0x600 #define CSR_HEDELEG 0x602 @@ -2864,6 +2872,8 @@ #define CSR_HTIMEDELTA 0x605 #define CSR_HCOUNTEREN 0x606 #define CSR_HGEIE 0x607 +#define CSR_HVIEN 0x608 +#define CSR_HVICTL 0x609 #define CSR_HENVCFG 0x60a #define CSR_HSTATEEN0 0x60c #define CSR_HSTATEEN1 0x60d @@ -2872,11 +2882,15 @@ #define CSR_HTVAL 0x643 #define CSR_HIP 0x644 #define CSR_HVIP 0x645 +#define CSR_HVIPRIO1 0x646 +#define CSR_HVIPRIO2 0x647 #define CSR_HTINST 0x64a #define CSR_HGATP 0x680 #define CSR_HCONTEXT 0x6a8 #define CSR_HGEIP 0xe12 +#define CSR_VSTOPI 0xeb0 #define CSR_SCOUNTOVF 0xda0 +#define CSR_STOPI 0xdb0 #define CSR_UTVT 0x7 #define CSR_UNXTI 0x45 #define CSR_UINTSTATUS 0x46 @@ -2899,6 +2913,8 @@ #define CSR_MIE 0x304 #define CSR_MTVEC 0x305 #define CSR_MCOUNTEREN 0x306 +#define CSR_MVIEN 0x308 +#define CSR_MVIP 0x309 #define CSR_MENVCFG 0x30a #define CSR_MSTATEEN0 0x30c #define CSR_MSTATEEN1 0x30d @@ -2912,6 +2928,9 @@ #define CSR_MIP 0x344 #define CSR_MTINST 0x34a #define CSR_MTVAL2 0x34b +#define CSR_MISELECT 0x350 +#define CSR_MIREG 0x351 +#define CSR_MTOPEI 0x35c #define CSR_PMPCFG0 0x3a0 #define CSR_PMPCFG1 0x3a1 #define CSR_PMPCFG2 0x3a2 @@ -3070,10 +3089,20 @@ #define CSR_MIMPID 0xf13 #define CSR_MHARTID 0xf14 #define CSR_MCONFIGPTR 0xf15 +#define CSR_MTOPI 0xfb0 +#define CSR_SIEH 0x114 +#define CSR_SIPH 0x154 #define CSR_STIMECMPH 0x15d +#define CSR_VSIEH 0x214 +#define CSR_VSIPH 0x254 #define CSR_VSTIMECMPH 0x25d #define CSR_HTIMEDELTAH 0x615 +#define CSR_HIDELEGH 0x613 +#define CSR_HVIENH 0x618 #define CSR_HENVCFGH 0x61a +#define CSR_HVIPH 0x655 +#define CSR_HVIPRIO1H 0x656 +#define CSR_HVIPRIO2H 0x657 #define CSR_HSTATEEN0H 0x61c #define CSR_HSTATEEN1H 0x61d #define CSR_HSTATEEN2H 0x61e @@ -3111,11 +3140,16 @@ #define CSR_HPMCOUNTER30H 0xc9e #define CSR_HPMCOUNTER31H 0xc9f #define CSR_MSTATUSH 0x310 +#define CSR_MIDELEGH 0x313 +#define CSR_MIEH 0x314 +#define CSR_MVIENH 0x318 +#define CSR_MVIPH 0x319 #define CSR_MENVCFGH 0x31a #define CSR_MSTATEEN0H 0x31c #define CSR_MSTATEEN1H 0x31d #define CSR_MSTATEEN2H 0x31e #define CSR_MSTATEEN3H 0x31f +#define CSR_MIPH 0x354 #define CSR_MHPMEVENT3H 0x723 #define CSR_MHPMEVENT4H 0x724 #define CSR_MHPMEVENT5H 0x725 @@ -3221,7 +3255,7 @@ #define INSN_FIELD_IMM12LO 0xf80 #define INSN_FIELD_BIMM12LO 0xf80 #define INSN_FIELD_ZIMM 0xf8000 -#define INSN_FIELD_SHAMT 0x7f00000 +#define INSN_FIELD_SHAMTQ 0x7f00000 #define INSN_FIELD_SHAMTW 0x1f00000 #define INSN_FIELD_SHAMTW4 0xf00000 #define INSN_FIELD_SHAMTD 0x3f00000 @@ -3276,6 +3310,11 @@ #define INSN_FIELD_C_UIMM9SPHI 0x1000 #define INSN_FIELD_C_UIMM10SP_S 0x1f80 #define INSN_FIELD_C_UIMM9SP_S 0x1f80 +#define INSN_FIELD_C_UIMM2 0x60 +#define INSN_FIELD_C_UIMM1 0x20 +#define INSN_FIELD_C_RLIST 0xf0 +#define INSN_FIELD_C_SPIMM 0xc +#define INSN_FIELD_C_INDEX 0x3fc #define INSN_FIELD_RS1_P 0x380 #define INSN_FIELD_RS2_P 0x1c #define INSN_FIELD_RD_P 0x1c @@ -3288,6 +3327,8 @@ #define INSN_FIELD_C_RS2_N0 0x7c #define INSN_FIELD_C_RS1_N0 0xf80 #define INSN_FIELD_C_RS2 0x7c +#define INSN_FIELD_C_SREG1 0x380 +#define INSN_FIELD_C_SREG2 0x1c #endif #ifdef DECLARE_INSN DECLARE_INSN(add, MATCH_ADD, MASK_ADD) @@ -3296,9 +3337,7 @@ DECLARE_INSN(add32, MATCH_ADD32, MASK_ADD32) DECLARE_INSN(add64, MATCH_ADD64, MASK_ADD64) DECLARE_INSN(add8, MATCH_ADD8, MASK_ADD8) DECLARE_INSN(add_uw, MATCH_ADD_UW, MASK_ADD_UW) -DECLARE_INSN(addd, MATCH_ADDD, MASK_ADDD) DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI) -DECLARE_INSN(addid, MATCH_ADDID, MASK_ADDID) DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW) DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW) DECLARE_INSN(aes32dsi, MATCH_AES32DSI, MASK_AES32DSI) @@ -3350,15 +3389,12 @@ DECLARE_INSN(bge, MATCH_BGE, MASK_BGE) DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU) DECLARE_INSN(binv, MATCH_BINV, MASK_BINV) DECLARE_INSN(binvi, MATCH_BINVI, MASK_BINVI) -DECLARE_INSN(bitrev, MATCH_BITREV, MASK_BITREV) -DECLARE_INSN(bitrevi, MATCH_BITREVI, MASK_BITREVI) DECLARE_INSN(blt, MATCH_BLT, MASK_BLT) DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU) DECLARE_INSN(bmatflip, MATCH_BMATFLIP, MASK_BMATFLIP) DECLARE_INSN(bmator, MATCH_BMATOR, MASK_BMATOR) DECLARE_INSN(bmatxor, MATCH_BMATXOR, MASK_BMATXOR) DECLARE_INSN(bne, MATCH_BNE, MASK_BNE) -DECLARE_INSN(bpick, MATCH_BPICK, MASK_BPICK) DECLARE_INSN(bset, MATCH_BSET, MASK_BSET) DECLARE_INSN(bseti, MATCH_BSETI, MASK_BSETI) DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD) @@ -3384,22 +3420,27 @@ DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J) DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL) DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR) DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR) +DECLARE_INSN(c_lbu, MATCH_C_LBU, MASK_C_LBU) DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD) DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP) +DECLARE_INSN(c_lh, MATCH_C_LH, MASK_C_LH) +DECLARE_INSN(c_lhu, MATCH_C_LHU, MASK_C_LHU) DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI) -DECLARE_INSN(c_lq, MATCH_C_LQ, MASK_C_LQ) -DECLARE_INSN(c_lqsp, MATCH_C_LQSP, MASK_C_LQSP) DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI) DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW) DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP) +DECLARE_INSN(c_mul, MATCH_C_MUL, MASK_C_MUL) DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV) DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP) +DECLARE_INSN(c_not, MATCH_C_NOT, MASK_C_NOT) DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR) +DECLARE_INSN(c_sb, MATCH_C_SB, MASK_C_SB) DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD) DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP) +DECLARE_INSN(c_sext_b, MATCH_C_SEXT_B, MASK_C_SEXT_B) +DECLARE_INSN(c_sext_h, MATCH_C_SEXT_H, MASK_C_SEXT_H) +DECLARE_INSN(c_sh, MATCH_C_SH, MASK_C_SH) DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI) -DECLARE_INSN(c_sq, MATCH_C_SQ, MASK_C_SQ) -DECLARE_INSN(c_sqsp, MATCH_C_SQSP, MASK_C_SQSP) DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI) DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI) DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB) @@ -3407,6 +3448,9 @@ DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW) DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW) DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP) DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR) +DECLARE_INSN(c_zext_b, MATCH_C_ZEXT_B, MASK_C_ZEXT_B) +DECLARE_INSN(c_zext_h, MATCH_C_ZEXT_H, MASK_C_ZEXT_H) +DECLARE_INSN(c_zext_w, MATCH_C_ZEXT_W, MASK_C_ZEXT_W) DECLARE_INSN(cbo_clean, MATCH_CBO_CLEAN, MASK_CBO_CLEAN) DECLARE_INSN(cbo_flush, MATCH_CBO_FLUSH, MASK_CBO_FLUSH) DECLARE_INSN(cbo_inval, MATCH_CBO_INVAL, MASK_CBO_INVAL) @@ -3414,9 +3458,6 @@ DECLARE_INSN(cbo_zero, MATCH_CBO_ZERO, MASK_CBO_ZERO) DECLARE_INSN(clmul, MATCH_CLMUL, MASK_CLMUL) DECLARE_INSN(clmulh, MATCH_CLMULH, MASK_CLMULH) DECLARE_INSN(clmulr, MATCH_CLMULR, MASK_CLMULR) -DECLARE_INSN(clo16, MATCH_CLO16, MASK_CLO16) -DECLARE_INSN(clo32, MATCH_CLO32, MASK_CLO32) -DECLARE_INSN(clo8, MATCH_CLO8, MASK_CLO8) DECLARE_INSN(clrs16, MATCH_CLRS16, MASK_CLRS16) DECLARE_INSN(clrs32, MATCH_CLRS32, MASK_CLRS32) DECLARE_INSN(clrs8, MATCH_CLRS8, MASK_CLRS8) @@ -3425,6 +3466,13 @@ DECLARE_INSN(clz16, MATCH_CLZ16, MASK_CLZ16) DECLARE_INSN(clz32, MATCH_CLZ32, MASK_CLZ32) DECLARE_INSN(clz8, MATCH_CLZ8, MASK_CLZ8) DECLARE_INSN(clzw, MATCH_CLZW, MASK_CLZW) +DECLARE_INSN(cm_jalt, MATCH_CM_JALT, MASK_CM_JALT) +DECLARE_INSN(cm_mva01s, MATCH_CM_MVA01S, MASK_CM_MVA01S) +DECLARE_INSN(cm_mvsa01, MATCH_CM_MVSA01, MASK_CM_MVSA01) +DECLARE_INSN(cm_pop, MATCH_CM_POP, MASK_CM_POP) +DECLARE_INSN(cm_popret, MATCH_CM_POPRET, MASK_CM_POPRET) +DECLARE_INSN(cm_popretz, MATCH_CM_POPRETZ, MASK_CM_POPRETZ) +DECLARE_INSN(cm_push, MATCH_CM_PUSH, MASK_CM_PUSH) DECLARE_INSN(cmix, MATCH_CMIX, MASK_CMIX) DECLARE_INSN(cmov, MATCH_CMOV, MASK_CMOV) DECLARE_INSN(cmpeq16, MATCH_CMPEQ16, MASK_CMPEQ16) @@ -3451,6 +3499,8 @@ DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW) DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI) DECLARE_INSN(ctz, MATCH_CTZ, MASK_CTZ) DECLARE_INSN(ctzw, MATCH_CTZW, MASK_CTZW) +DECLARE_INSN(czero_eqz, MATCH_CZERO_EQZ, MASK_CZERO_EQZ) +DECLARE_INSN(czero_nez, MATCH_CZERO_NEZ, MASK_CZERO_NEZ) DECLARE_INSN(div, MATCH_DIV, MASK_DIV) DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU) DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW) @@ -3732,10 +3782,8 @@ DECLARE_INSN(kwmmul_u, MATCH_KWMMUL_U, MASK_KWMMUL_U) DECLARE_INSN(lb, MATCH_LB, MASK_LB) DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU) DECLARE_INSN(ld, MATCH_LD, MASK_LD) -DECLARE_INSN(ldu, MATCH_LDU, MASK_LDU) DECLARE_INSN(lh, MATCH_LH, MASK_LH) DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU) -DECLARE_INSN(lq, MATCH_LQ, MASK_LQ) DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D) DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W) DECLARE_INSN(lui, MATCH_LUI, MASK_LUI) @@ -3744,10 +3792,8 @@ DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU) DECLARE_INSN(maddr32, MATCH_MADDR32, MASK_MADDR32) DECLARE_INSN(max, MATCH_MAX, MASK_MAX) DECLARE_INSN(maxu, MATCH_MAXU, MASK_MAXU) -DECLARE_INSN(maxw, MATCH_MAXW, MASK_MAXW) DECLARE_INSN(min, MATCH_MIN, MASK_MIN) DECLARE_INSN(minu, MATCH_MINU, MASK_MINU) -DECLARE_INSN(minw, MATCH_MINW, MASK_MINW) DECLARE_INSN(mret, MATCH_MRET, MASK_MRET) DECLARE_INSN(msubr32, MATCH_MSUBR32, MASK_MSUBR32) DECLARE_INSN(mul, MATCH_MUL, MASK_MUL) @@ -3769,13 +3815,11 @@ DECLARE_INSN(pause, MATCH_PAUSE, MASK_PAUSE) DECLARE_INSN(pbsad, MATCH_PBSAD, MASK_PBSAD) DECLARE_INSN(pbsada, MATCH_PBSADA, MASK_PBSADA) DECLARE_INSN(pkbb16, MATCH_PKBB16, MASK_PKBB16) -DECLARE_INSN(pkbb32, MATCH_PKBB32, MASK_PKBB32) DECLARE_INSN(pkbt16, MATCH_PKBT16, MASK_PKBT16) DECLARE_INSN(pkbt32, MATCH_PKBT32, MASK_PKBT32) DECLARE_INSN(pktb16, MATCH_PKTB16, MASK_PKTB16) DECLARE_INSN(pktb32, MATCH_PKTB32, MASK_PKTB32) DECLARE_INSN(pktt16, MATCH_PKTT16, MASK_PKTT16) -DECLARE_INSN(pktt32, MATCH_PKTT32, MASK_PKTT32) DECLARE_INSN(prefetch_i, MATCH_PREFETCH_I, MASK_PREFETCH_I) DECLARE_INSN(prefetch_r, MATCH_PREFETCH_R, MASK_PREFETCH_R) DECLARE_INSN(prefetch_w, MATCH_PREFETCH_W, MASK_PREFETCH_W) @@ -3852,13 +3896,12 @@ DECLARE_INSN(sll, MATCH_SLL, MASK_SLL) DECLARE_INSN(sll16, MATCH_SLL16, MASK_SLL16) DECLARE_INSN(sll32, MATCH_SLL32, MASK_SLL32) DECLARE_INSN(sll8, MATCH_SLL8, MASK_SLL8) -DECLARE_INSN(slld, MATCH_SLLD, MASK_SLLD) DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI) DECLARE_INSN(slli16, MATCH_SLLI16, MASK_SLLI16) DECLARE_INSN(slli32, MATCH_SLLI32, MASK_SLLI32) DECLARE_INSN(slli8, MATCH_SLLI8, MASK_SLLI8) +DECLARE_INSN(slli_rv32, MATCH_SLLI_RV32, MASK_SLLI_RV32) DECLARE_INSN(slli_uw, MATCH_SLLI_UW, MASK_SLLI_UW) -DECLARE_INSN(sllid, MATCH_SLLID, MASK_SLLID) DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW) DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW) DECLARE_INSN(slo, MATCH_SLO, MASK_SLO) @@ -3915,7 +3958,6 @@ DECLARE_INSN(smulx16, MATCH_SMULX16, MASK_SMULX16) DECLARE_INSN(smulx8, MATCH_SMULX8, MASK_SMULX8) DECLARE_INSN(smxds, MATCH_SMXDS, MASK_SMXDS) DECLARE_INSN(smxds32, MATCH_SMXDS32, MASK_SMXDS32) -DECLARE_INSN(sq, MATCH_SQ, MASK_SQ) DECLARE_INSN(sra, MATCH_SRA, MASK_SRA) DECLARE_INSN(sra16, MATCH_SRA16, MASK_SRA16) DECLARE_INSN(sra16_u, MATCH_SRA16_U, MASK_SRA16_U) @@ -3924,7 +3966,6 @@ DECLARE_INSN(sra32_u, MATCH_SRA32_U, MASK_SRA32_U) DECLARE_INSN(sra8, MATCH_SRA8, MASK_SRA8) DECLARE_INSN(sra8_u, MATCH_SRA8_U, MASK_SRA8_U) DECLARE_INSN(sra_u, MATCH_SRA_U, MASK_SRA_U) -DECLARE_INSN(srad, MATCH_SRAD, MASK_SRAD) DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI) DECLARE_INSN(srai16, MATCH_SRAI16, MASK_SRAI16) DECLARE_INSN(srai16_u, MATCH_SRAI16_U, MASK_SRAI16_U) @@ -3932,8 +3973,8 @@ DECLARE_INSN(srai32, MATCH_SRAI32, MASK_SRAI32) DECLARE_INSN(srai32_u, MATCH_SRAI32_U, MASK_SRAI32_U) DECLARE_INSN(srai8, MATCH_SRAI8, MASK_SRAI8) DECLARE_INSN(srai8_u, MATCH_SRAI8_U, MASK_SRAI8_U) +DECLARE_INSN(srai_rv32, MATCH_SRAI_RV32, MASK_SRAI_RV32) DECLARE_INSN(srai_u, MATCH_SRAI_U, MASK_SRAI_U) -DECLARE_INSN(sraid, MATCH_SRAID, MASK_SRAID) DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW) DECLARE_INSN(sraiw_u, MATCH_SRAIW_U, MASK_SRAIW_U) DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW) @@ -3945,7 +3986,6 @@ DECLARE_INSN(srl32, MATCH_SRL32, MASK_SRL32) DECLARE_INSN(srl32_u, MATCH_SRL32_U, MASK_SRL32_U) DECLARE_INSN(srl8, MATCH_SRL8, MASK_SRL8) DECLARE_INSN(srl8_u, MATCH_SRL8_U, MASK_SRL8_U) -DECLARE_INSN(srld, MATCH_SRLD, MASK_SRLD) DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI) DECLARE_INSN(srli16, MATCH_SRLI16, MASK_SRLI16) DECLARE_INSN(srli16_u, MATCH_SRLI16_U, MASK_SRLI16_U) @@ -3953,7 +3993,7 @@ DECLARE_INSN(srli32, MATCH_SRLI32, MASK_SRLI32) DECLARE_INSN(srli32_u, MATCH_SRLI32_U, MASK_SRLI32_U) DECLARE_INSN(srli8, MATCH_SRLI8, MASK_SRLI8) DECLARE_INSN(srli8_u, MATCH_SRLI8_U, MASK_SRLI8_U) -DECLARE_INSN(srlid, MATCH_SRLID, MASK_SRLID) +DECLARE_INSN(srli_rv32, MATCH_SRLI_RV32, MASK_SRLI_RV32) DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW) DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW) DECLARE_INSN(sro, MATCH_SRO, MASK_SRO) @@ -3969,7 +4009,6 @@ DECLARE_INSN(sub16, MATCH_SUB16, MASK_SUB16) DECLARE_INSN(sub32, MATCH_SUB32, MASK_SUB32) DECLARE_INSN(sub64, MATCH_SUB64, MASK_SUB64) DECLARE_INSN(sub8, MATCH_SUB8, MASK_SUB8) -DECLARE_INSN(subd, MATCH_SUBD, MASK_SUBD) DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW) DECLARE_INSN(sunpkd810, MATCH_SUNPKD810, MASK_SUNPKD810) DECLARE_INSN(sunpkd820, MATCH_SUNPKD820, MASK_SUNPKD820) @@ -3977,7 +4016,6 @@ DECLARE_INSN(sunpkd830, MATCH_SUNPKD830, MASK_SUNPKD830) DECLARE_INSN(sunpkd831, MATCH_SUNPKD831, MASK_SUNPKD831) DECLARE_INSN(sunpkd832, MATCH_SUNPKD832, MASK_SUNPKD832) DECLARE_INSN(sw, MATCH_SW, MASK_SW) -DECLARE_INSN(swap8, MATCH_SWAP8, MASK_SWAP8) DECLARE_INSN(uclip16, MATCH_UCLIP16, MASK_UCLIP16) DECLARE_INSN(uclip32, MATCH_UCLIP32, MASK_UCLIP32) DECLARE_INSN(uclip8, MATCH_UCLIP8, MASK_UCLIP8) @@ -4488,8 +4526,6 @@ DECLARE_INSN(vxor_vx, MATCH_VXOR_VX, MASK_VXOR_VX) DECLARE_INSN(vzext_vf2, MATCH_VZEXT_VF2, MASK_VZEXT_VF2) DECLARE_INSN(vzext_vf4, MATCH_VZEXT_VF4, MASK_VZEXT_VF4) DECLARE_INSN(vzext_vf8, MATCH_VZEXT_VF8, MASK_VZEXT_VF8) -DECLARE_INSN(wext, MATCH_WEXT, MASK_WEXT) -DECLARE_INSN(wexti, MATCH_WEXTI, MASK_WEXTI) DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI) DECLARE_INSN(wrs_nto, MATCH_WRS_NTO, MASK_WRS_NTO) DECLARE_INSN(wrs_sto, MATCH_WRS_STO, MASK_WRS_STO) @@ -4515,6 +4551,7 @@ DECLARE_CSR(vxsat, CSR_VXSAT) DECLARE_CSR(vxrm, CSR_VXRM) DECLARE_CSR(vcsr, CSR_VCSR) DECLARE_CSR(seed, CSR_SEED) +DECLARE_CSR(jvt, CSR_JVT) DECLARE_CSR(cycle, CSR_CYCLE) DECLARE_CSR(time, CSR_TIME) DECLARE_CSR(instret, CSR_INSTRET) @@ -4567,6 +4604,9 @@ DECLARE_CSR(scause, CSR_SCAUSE) DECLARE_CSR(stval, CSR_STVAL) DECLARE_CSR(sip, CSR_SIP) DECLARE_CSR(stimecmp, CSR_STIMECMP) +DECLARE_CSR(siselect, CSR_SISELECT) +DECLARE_CSR(sireg, CSR_SIREG) +DECLARE_CSR(stopei, CSR_STOPEI) DECLARE_CSR(satp, CSR_SATP) DECLARE_CSR(scontext, CSR_SCONTEXT) DECLARE_CSR(vsstatus, CSR_VSSTATUS) @@ -4578,6 +4618,9 @@ DECLARE_CSR(vscause, CSR_VSCAUSE) DECLARE_CSR(vstval, CSR_VSTVAL) DECLARE_CSR(vsip, CSR_VSIP) DECLARE_CSR(vstimecmp, CSR_VSTIMECMP) +DECLARE_CSR(vsiselect, CSR_VSISELECT) +DECLARE_CSR(vsireg, CSR_VSIREG) +DECLARE_CSR(vstopei, CSR_VSTOPEI) DECLARE_CSR(vsatp, CSR_VSATP) DECLARE_CSR(hstatus, CSR_HSTATUS) DECLARE_CSR(hedeleg, CSR_HEDELEG) @@ -4586,6 +4629,8 @@ DECLARE_CSR(hie, CSR_HIE) DECLARE_CSR(htimedelta, CSR_HTIMEDELTA) DECLARE_CSR(hcounteren, CSR_HCOUNTEREN) DECLARE_CSR(hgeie, CSR_HGEIE) +DECLARE_CSR(hvien, CSR_HVIEN) +DECLARE_CSR(hvictl, CSR_HVICTL) DECLARE_CSR(henvcfg, CSR_HENVCFG) DECLARE_CSR(hstateen0, CSR_HSTATEEN0) DECLARE_CSR(hstateen1, CSR_HSTATEEN1) @@ -4594,11 +4639,15 @@ DECLARE_CSR(hstateen3, CSR_HSTATEEN3) DECLARE_CSR(htval, CSR_HTVAL) DECLARE_CSR(hip, CSR_HIP) DECLARE_CSR(hvip, CSR_HVIP) +DECLARE_CSR(hviprio1, CSR_HVIPRIO1) +DECLARE_CSR(hviprio2, CSR_HVIPRIO2) DECLARE_CSR(htinst, CSR_HTINST) DECLARE_CSR(hgatp, CSR_HGATP) DECLARE_CSR(hcontext, CSR_HCONTEXT) DECLARE_CSR(hgeip, CSR_HGEIP) +DECLARE_CSR(vstopi, CSR_VSTOPI) DECLARE_CSR(scountovf, CSR_SCOUNTOVF) +DECLARE_CSR(stopi, CSR_STOPI) DECLARE_CSR(utvt, CSR_UTVT) DECLARE_CSR(unxti, CSR_UNXTI) DECLARE_CSR(uintstatus, CSR_UINTSTATUS) @@ -4621,6 +4670,8 @@ DECLARE_CSR(mideleg, CSR_MIDELEG) DECLARE_CSR(mie, CSR_MIE) DECLARE_CSR(mtvec, CSR_MTVEC) DECLARE_CSR(mcounteren, CSR_MCOUNTEREN) +DECLARE_CSR(mvien, CSR_MVIEN) +DECLARE_CSR(mvip, CSR_MVIP) DECLARE_CSR(menvcfg, CSR_MENVCFG) DECLARE_CSR(mstateen0, CSR_MSTATEEN0) DECLARE_CSR(mstateen1, CSR_MSTATEEN1) @@ -4634,6 +4685,9 @@ DECLARE_CSR(mtval, CSR_MTVAL) DECLARE_CSR(mip, CSR_MIP) DECLARE_CSR(mtinst, CSR_MTINST) DECLARE_CSR(mtval2, CSR_MTVAL2) +DECLARE_CSR(miselect, CSR_MISELECT) +DECLARE_CSR(mireg, CSR_MIREG) +DECLARE_CSR(mtopei, CSR_MTOPEI) DECLARE_CSR(pmpcfg0, CSR_PMPCFG0) DECLARE_CSR(pmpcfg1, CSR_PMPCFG1) DECLARE_CSR(pmpcfg2, CSR_PMPCFG2) @@ -4792,10 +4846,20 @@ DECLARE_CSR(marchid, CSR_MARCHID) DECLARE_CSR(mimpid, CSR_MIMPID) DECLARE_CSR(mhartid, CSR_MHARTID) DECLARE_CSR(mconfigptr, CSR_MCONFIGPTR) +DECLARE_CSR(mtopi, CSR_MTOPI) +DECLARE_CSR(sieh, CSR_SIEH) +DECLARE_CSR(siph, CSR_SIPH) DECLARE_CSR(stimecmph, CSR_STIMECMPH) +DECLARE_CSR(vsieh, CSR_VSIEH) +DECLARE_CSR(vsiph, CSR_VSIPH) DECLARE_CSR(vstimecmph, CSR_VSTIMECMPH) DECLARE_CSR(htimedeltah, CSR_HTIMEDELTAH) +DECLARE_CSR(hidelegh, CSR_HIDELEGH) +DECLARE_CSR(hvienh, CSR_HVIENH) DECLARE_CSR(henvcfgh, CSR_HENVCFGH) +DECLARE_CSR(hviph, CSR_HVIPH) +DECLARE_CSR(hviprio1h, CSR_HVIPRIO1H) +DECLARE_CSR(hviprio2h, CSR_HVIPRIO2H) DECLARE_CSR(hstateen0h, CSR_HSTATEEN0H) DECLARE_CSR(hstateen1h, CSR_HSTATEEN1H) DECLARE_CSR(hstateen2h, CSR_HSTATEEN2H) @@ -4833,11 +4897,16 @@ DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H) DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H) DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H) DECLARE_CSR(mstatush, CSR_MSTATUSH) +DECLARE_CSR(midelegh, CSR_MIDELEGH) +DECLARE_CSR(mieh, CSR_MIEH) +DECLARE_CSR(mvienh, CSR_MVIENH) +DECLARE_CSR(mviph, CSR_MVIPH) DECLARE_CSR(menvcfgh, CSR_MENVCFGH) DECLARE_CSR(mstateen0h, CSR_MSTATEEN0H) DECLARE_CSR(mstateen1h, CSR_MSTATEEN1H) DECLARE_CSR(mstateen2h, CSR_MSTATEEN2H) DECLARE_CSR(mstateen3h, CSR_MSTATEEN3H) +DECLARE_CSR(miph, CSR_MIPH) DECLARE_CSR(mhpmevent3h, CSR_MHPMEVENT3H) DECLARE_CSR(mhpmevent4h, CSR_MHPMEVENT4H) DECLARE_CSR(mhpmevent5h, CSR_MHPMEVENT5H) diff --git a/src/target/riscv/field_helpers.h b/src/target/riscv/field_helpers.h new file mode 100644 index 000000000..abf19f677 --- /dev/null +++ b/src/target/riscv/field_helpers.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_FIELD_HELPERS_H +#define OPENOCD_TARGET_RISCV_FIELD_HELPERS_H + +#include +#include + +static inline uint64_t get_field(uint64_t reg, uint64_t mask) +{ + return (reg & mask) / (mask & ~(mask << 1)); +} + +static inline uint32_t get_field32(uint64_t reg, uint64_t mask) +{ + uint64_t value = get_field(reg, mask); + assert(value <= UINT32_MAX); + return value; +} + +static inline uint64_t set_field(uint64_t reg, uint64_t mask, uint64_t val) +{ + /* Clear current value from field. */ + reg &= ~mask; + uint64_t low_field_bit = mask & ~(mask << 1); + /* Assert if the value doesn't fit in the field. */ + assert(((val * low_field_bit) & ~mask) == 0); + reg |= (val * low_field_bit) & mask; + return reg; +} + +static inline uint32_t set_field32(uint32_t reg, uint32_t mask, uint32_t val) +{ + return (uint32_t)set_field(reg, mask, val); +} + +static inline uint64_t field_value(uint64_t mask, uint64_t val) +{ + return set_field(0, mask, val); +} + +static inline uint32_t field_value32(uint32_t mask, uint32_t val) +{ + return set_field32(0, mask, val); +} + +#endif /* OPENOCD_TARGET_RISCV_FIELD_HELPERS_H */ diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h index 32bc1d577..0d0392912 100644 --- a/src/target/riscv/gdb_regs.h +++ b/src/target/riscv/gdb_regs.h @@ -1,7 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef TARGET__RISCV__GDB_REGS_H -#define TARGET__RISCV__GDB_REGS_H +#ifndef OPENOCD_TARGET_RISCV_GDB_REGS_H +#define OPENOCD_TARGET_RISCV_GDB_REGS_H + +#include "encoding.h" /* gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in * its source tree. We must interpret the numbers the same here. */ @@ -78,15 +80,21 @@ enum gdb_regno { GDB_REGNO_FT11, GDB_REGNO_FPR31 = GDB_REGNO_FT11, GDB_REGNO_CSR0 = 65, + GDB_REGNO_FCSR = CSR_FCSR + GDB_REGNO_CSR0, + GDB_REGNO_FFLAGS = CSR_FFLAGS + GDB_REGNO_CSR0, + GDB_REGNO_FRM = CSR_FRM + GDB_REGNO_CSR0, GDB_REGNO_VSTART = CSR_VSTART + GDB_REGNO_CSR0, GDB_REGNO_VXSAT = CSR_VXSAT + GDB_REGNO_CSR0, GDB_REGNO_VXRM = CSR_VXRM + GDB_REGNO_CSR0, + GDB_REGNO_VCSR = CSR_VCSR + GDB_REGNO_CSR0, GDB_REGNO_VLENB = CSR_VLENB + GDB_REGNO_CSR0, GDB_REGNO_VL = CSR_VL + GDB_REGNO_CSR0, GDB_REGNO_VTYPE = CSR_VTYPE + GDB_REGNO_CSR0, GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0, GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0, GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0, + GDB_REGNO_TDATA3 = CSR_TDATA3 + GDB_REGNO_CSR0, + GDB_REGNO_TINFO = CSR_TINFO + GDB_REGNO_CSR0, GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0, GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0, GDB_REGNO_DCSR = CSR_DCSR + GDB_REGNO_CSR0, @@ -95,6 +103,11 @@ enum gdb_regno { GDB_REGNO_MEPC = CSR_MEPC + GDB_REGNO_CSR0, GDB_REGNO_MCAUSE = CSR_MCAUSE + GDB_REGNO_CSR0, GDB_REGNO_SATP = CSR_SATP + GDB_REGNO_CSR0, + GDB_REGNO_VSATP = CSR_VSATP + GDB_REGNO_CSR0, + GDB_REGNO_HGATP = CSR_HGATP + GDB_REGNO_CSR0, + GDB_REGNO_HSTATUS = CSR_HSTATUS + GDB_REGNO_CSR0, + GDB_REGNO_MTOPI = CSR_MTOPI + GDB_REGNO_CSR0, + GDB_REGNO_MTOPEI = CSR_MTOPEI + GDB_REGNO_CSR0, GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095, GDB_REGNO_PRIV = 4161, /* It's still undecided what register numbers GDB will actually use for @@ -112,6 +125,4 @@ enum gdb_regno { GDB_REGNO_COUNT }; -const char *gdb_regno_name(enum gdb_regno regno); - -#endif +#endif /* OPENOCD_TARGET_RISCV_GDB_REGS_H */ diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h index 7d17e6eea..e4efc5b05 100644 --- a/src/target/riscv/opcodes.h +++ b/src/target/riscv/opcodes.h @@ -1,15 +1,39 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef OPENOCD_TARGET_RISCV_OPCODES_H +#define OPENOCD_TARGET_RISCV_OPCODES_H + #include "encoding.h" +#include +#include +#include #define ZERO 0 #define T0 5 #define S0 8 #define S1 9 +#define MAX_GPR_NUM 31 +#define MAX_FPR_NUM 31 +#define MAX_VREG_NUM 31 +#define MAX_CSR_NUM 4095 + +#define MIN_INT12 (-0x800) +#define MAX_INT12 0x7ff + +#define MIN_INT13 (-0x1000) +#define MAX_INT13 0xfff + +#define MIN_INT21 (-0x100000) +#define MAX_INT21 0xfffff + +#define MAX_UINT5 0x1f +#define MAX_UINT11 0x7ff +#define MAX_UINT12 0xfff + static uint32_t bits(uint32_t value, unsigned int hi, unsigned int lo) { - return (value >> lo) & ((1 << (hi+1-lo)) - 1); + return (value >> lo) & (((uint32_t)1 << (hi + 1 - lo)) - 1); } static uint32_t bit(uint32_t value, unsigned int b) @@ -65,153 +89,246 @@ static uint32_t imm_j(uint32_t imm) return (bits(imm, 19, 12) << 12) | (bit(imm, 11) << 20) | (bits(imm, 10, 1) << 21) | (bit(imm, 20) << 31); } -static uint32_t jal(unsigned int rd, uint32_t imm) __attribute__ ((unused)); -static uint32_t jal(unsigned int rd, uint32_t imm) +static uint32_t jal(unsigned int rd, int32_t imm) __attribute__ ((unused)); +static uint32_t jal(unsigned int rd, int32_t imm) { - return imm_j(imm) | inst_rd(rd) | MATCH_JAL; + assert(rd <= MAX_GPR_NUM); + assert((imm >= MIN_INT21) && (imm <= MAX_INT21)); + assert((imm & 1) == 0); + + return imm_j((uint32_t)imm) | inst_rd(rd) | MATCH_JAL; } -static uint32_t csrsi(unsigned int csr, uint16_t imm) __attribute__ ((unused)); -static uint32_t csrsi(unsigned int csr, uint16_t imm) +static uint32_t csrsi(unsigned int csr, uint8_t imm) __attribute__ ((unused)); +static uint32_t csrsi(unsigned int csr, uint8_t imm) { + assert(csr <= MAX_CSR_NUM); + assert(imm <= MAX_UINT5); + return imm_i(csr) | inst_rs1(imm) | MATCH_CSRRSI; } -static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) +static uint32_t sw(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t sw(unsigned int src, unsigned int base, int16_t offset) { - return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SW; + assert(src <= MAX_GPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SW; } -static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) +static uint32_t sd(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t sd(unsigned int src, unsigned int base, int16_t offset) { - return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SD; + assert(src <= MAX_GPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SD; } -static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) +static uint32_t sh(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t sh(unsigned int src, unsigned int base, int16_t offset) { - return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SH; + assert(src <= MAX_GPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SH; } -static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) +static uint32_t sb(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t sb(unsigned int src, unsigned int base, int16_t offset) { - return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SB; + assert(src <= MAX_GPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SB; } -static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) +static uint32_t ld(unsigned int rd, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t ld(unsigned int rd, unsigned int base, int16_t offset) { - return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LD; + assert(rd <= MAX_GPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LD; } -static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) +static uint32_t lw(unsigned int rd, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t lw(unsigned int rd, unsigned int base, int16_t offset) { - return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LW; + assert(rd <= MAX_GPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LW; } -static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) +static uint32_t lh(unsigned int rd, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t lh(unsigned int rd, unsigned int base, int16_t offset) { - return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LH; + assert(rd <= MAX_GPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LH; } -static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) +static uint32_t lb(unsigned int rd, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t lb(unsigned int rd, unsigned int base, int16_t offset) { - return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LB; + assert(rd <= MAX_GPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LB; } static uint32_t csrw(unsigned int source, unsigned int csr) __attribute__ ((unused)); static uint32_t csrw(unsigned int source, unsigned int csr) { + assert(source <= MAX_GPR_NUM); + assert(csr <= MAX_CSR_NUM); + return imm_i(csr) | inst_rs1(source) | MATCH_CSRRW; } -static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused)); -static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) +static uint32_t addi(unsigned int dest, unsigned int src, int16_t imm) __attribute__ ((unused)); +static uint32_t addi(unsigned int dest, unsigned int src, int16_t imm) { - return imm_i(imm) | inst_rs1(src) | inst_rd(dest) | MATCH_ADDI; + assert(dest <= MAX_GPR_NUM); + assert(src <= MAX_GPR_NUM); + assert((imm >= MIN_INT12) && (imm <= MAX_INT12)); + + return imm_i((uint16_t)imm) | inst_rs1(src) | inst_rd(dest) | MATCH_ADDI; } static uint32_t csrr(unsigned int rd, unsigned int csr) __attribute__ ((unused)); static uint32_t csrr(unsigned int rd, unsigned int csr) { + assert(rd <= MAX_GPR_NUM); + assert(csr <= MAX_CSR_NUM); + return imm_i(csr) | inst_rd(rd) | MATCH_CSRRS; } static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused)); static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) { + assert(rd <= MAX_GPR_NUM); + assert(rs <= MAX_GPR_NUM); + assert(csr <= MAX_CSR_NUM); + return imm_i(csr) | inst_rs1(rs) | inst_rd(rd) | MATCH_CSRRS; } static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused)); static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) { + assert(rd <= MAX_GPR_NUM); + assert(rs <= MAX_GPR_NUM); + assert(csr <= MAX_CSR_NUM); + return imm_i(csr) | inst_rs1(rs) | inst_rd(rd) | MATCH_CSRRW; } -static uint32_t csrrci(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused)); -static uint32_t csrrci(unsigned int rd, unsigned int zimm, unsigned int csr) +static uint32_t csrrci(unsigned int rd, uint8_t zimm, unsigned int csr) __attribute__ ((unused)); +static uint32_t csrrci(unsigned int rd, uint8_t zimm, unsigned int csr) { + assert(rd <= MAX_GPR_NUM); + assert(zimm <= MAX_UINT5); + assert(csr <= MAX_CSR_NUM); + return imm_i(csr) | inst_rs1(zimm) | inst_rd(rd) | MATCH_CSRRCI; } -static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused)); -static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr) +static uint32_t csrrsi(unsigned int rd, uint8_t zimm, unsigned int csr) __attribute__ ((unused)); +static uint32_t csrrsi(unsigned int rd, uint8_t zimm, unsigned int csr) { + assert(rd <= MAX_GPR_NUM); + assert(zimm <= MAX_UINT5); + assert(csr <= MAX_CSR_NUM); + return imm_i(csr) | inst_rs1(zimm) | inst_rd(rd) | MATCH_CSRRSI; } -static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) +static uint32_t fsw(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t fsw(unsigned int src, unsigned int base, int16_t offset) { - return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSW; + assert(src <= MAX_FPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSW; } -static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) +static uint32_t fsd(unsigned int src, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t fsd(unsigned int src, unsigned int base, int16_t offset) { - return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSD; + assert(src <= MAX_FPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_s((uint16_t)offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSD; } -static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) +static uint32_t flw(unsigned int dest, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t flw(unsigned int dest, unsigned int base, int16_t offset) { - return imm_i(offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLW; + assert(dest <= MAX_FPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLW; } -static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) +static uint32_t fld(unsigned int dest, unsigned int base, int16_t offset) __attribute__ ((unused)); +static uint32_t fld(unsigned int dest, unsigned int base, int16_t offset) { - return imm_i(offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLD; + assert(dest <= MAX_FPR_NUM); + assert(base <= MAX_GPR_NUM); + assert((offset >= MIN_INT12) && (offset <= MAX_INT12)); + + return imm_i((uint16_t)offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLD; } static uint32_t fmv_x_w(unsigned int dest, unsigned int src) __attribute__ ((unused)); static uint32_t fmv_x_w(unsigned int dest, unsigned int src) { + assert(dest <= MAX_GPR_NUM); + assert(src <= MAX_FPR_NUM); + return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_X_W; } static uint32_t fmv_x_d(unsigned int dest, unsigned int src) __attribute__ ((unused)); static uint32_t fmv_x_d(unsigned int dest, unsigned int src) { + assert(dest <= MAX_GPR_NUM); + assert(src <= MAX_FPR_NUM); + return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_X_D; } static uint32_t fmv_w_x(unsigned int dest, unsigned int src) __attribute__ ((unused)); static uint32_t fmv_w_x(unsigned int dest, unsigned int src) { + assert(dest <= MAX_FPR_NUM); + assert(src <= MAX_GPR_NUM); + return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_W_X; } static uint32_t fmv_d_x(unsigned int dest, unsigned int src) __attribute__ ((unused)); static uint32_t fmv_d_x(unsigned int dest, unsigned int src) { + assert(dest <= MAX_FPR_NUM); + assert(src <= MAX_GPR_NUM); + return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_D_X; } @@ -238,97 +355,95 @@ static uint32_t fence_i(void) static uint32_t lui(unsigned int dest, uint32_t imm) __attribute__ ((unused)); static uint32_t lui(unsigned int dest, uint32_t imm) { + assert(dest <= MAX_GPR_NUM); + assert(bits(imm, 11, 0) == 0); + return imm_u(imm) | inst_rd(dest) | MATCH_LUI; } -/* -static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused)); -static uint32_t csrci(unsigned int csr, uint16_t imm) +static uint32_t xori(unsigned int dest, unsigned int src, int16_t imm) __attribute__ ((unused)); +static uint32_t xori(unsigned int dest, unsigned int src, int16_t imm) { - return (csr << 20) | - (bits(imm, 4, 0) << 15) | - MATCH_CSRRCI; -} + assert(dest <= MAX_GPR_NUM); + assert(src <= MAX_GPR_NUM); + assert((imm >= MIN_INT12) && (imm <= MAX_INT12)); -static uint32_t li(unsigned int dest, uint16_t imm) __attribute__ ((unused)); -static uint32_t li(unsigned int dest, uint16_t imm) -{ - return addi(dest, 0, imm); -} - -static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); -static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) -{ - return (bits(offset, 11, 5) << 25) | - (bits(src, 4, 0) << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_FSD; -} - -static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused)); -static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm) -{ - return (bits(imm, 11, 0) << 20) | - (src << 15) | - (dest << 7) | - MATCH_ORI; -} - -static uint32_t nop(void) __attribute__ ((unused)); -static uint32_t nop(void) -{ - return addi(0, 0, 0); -} -*/ - -static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused)); -static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) -{ - return imm_i(imm) | inst_rs1(src) | inst_rd(dest) | MATCH_XORI; + return imm_i((uint16_t)imm) | inst_rs1(src) | inst_rd(dest) | MATCH_XORI; } static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) __attribute__ ((unused)); static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) { + assert(dest <= MAX_GPR_NUM); + assert(src <= MAX_GPR_NUM); + assert(shamt <= MAX_UINT5); + return inst_rs2(shamt) | inst_rs1(src) | inst_rd(dest) | MATCH_SRLI; } -static uint32_t fence(void) __attribute__((unused)); -static uint32_t fence(void) +static uint32_t fence_rw_rw(void) __attribute__((unused)); +static uint32_t fence_rw_rw(void) { - return MATCH_FENCE; + /* fence rw,rw */ + return MATCH_FENCE | 0x3300000; } static uint32_t auipc(unsigned int dest) __attribute__((unused)); static uint32_t auipc(unsigned int dest) { + assert(dest <= MAX_GPR_NUM); + return MATCH_AUIPC | inst_rd(dest); } -static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm) __attribute__((unused)); -static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm) +static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t vtypei) __attribute__((unused)); +static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t vtypei) { - return (bits(imm, 10, 0) << 20) | inst_rs1(src) | inst_rd(dest) | MATCH_VSETVLI; + assert(dest <= MAX_GPR_NUM); + assert(src <= MAX_GPR_NUM); + assert(vtypei <= MAX_UINT11); + + return (bits(vtypei, 10, 0) << 20) | inst_rs1(src) | inst_rd(dest) | MATCH_VSETVLI; +} + +static uint32_t vsetvl(unsigned int rd, unsigned int rs1, unsigned int rs2) __attribute__((unused)); +static uint32_t vsetvl(unsigned int rd, unsigned int rs1, unsigned int rs2) +{ + assert(rd <= MAX_GPR_NUM); + assert(rs1 <= MAX_GPR_NUM); + assert(rs2 <= MAX_GPR_NUM); + + return inst_rd(rd) | inst_rs1(rs1) | inst_rs2(rs2) | MATCH_VSETVL; } static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) __attribute__((unused)); static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) { + assert(rd <= MAX_GPR_NUM); + assert(vs2 <= MAX_VREG_NUM); + return inst_rs2(vs2) | inst_rd(rd) | MATCH_VMV_X_S; } -static uint32_t vmv_s_x(unsigned int vd, unsigned int vs2) __attribute__((unused)); -static uint32_t vmv_s_x(unsigned int vd, unsigned int rs1) +static uint32_t vmv_s_x(unsigned int vd, unsigned int rs2) __attribute__((unused)); +static uint32_t vmv_s_x(unsigned int vd, unsigned int rs2) { - return inst_rs1(rs1) | inst_rd(vd) | MATCH_VMV_S_X; + assert(vd <= MAX_VREG_NUM); + assert(rs2 <= MAX_GPR_NUM); + + return inst_rs1(rs2) | inst_rd(vd) | MATCH_VMV_S_X; } static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2, - unsigned int rs1, unsigned int vm) __attribute__((unused)); + unsigned int rs1, bool vm) __attribute__((unused)); static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2, - unsigned int rs1, unsigned int vm) + unsigned int rs1, bool vm) { - return ((vm & 1) << 25) | inst_rs2(vs2) | inst_rs1(rs1) | inst_rd(vd) | MATCH_VSLIDE1DOWN_VX; + assert(vd <= MAX_VREG_NUM); + assert(vs2 <= MAX_VREG_NUM); + assert(rs1 <= MAX_GPR_NUM); + + return (vm ? (1u << 25) : 0u) | inst_rs2(vs2) | inst_rs1(rs1) | inst_rd(vd) | MATCH_VSLIDE1DOWN_VX; } +#endif /* OPENOCD_TARGET_RISCV_OPCODES_H */ diff --git a/src/target/riscv/program.c b/src/target/riscv/program.c index 1014137f4..bf6718f4e 100644 --- a/src/target/riscv/program.c +++ b/src/target/riscv/program.c @@ -10,7 +10,7 @@ #include "program.h" #include "helper/log.h" -#include "asm.h" +#include "debug_defines.h" #include "encoding.h" /* Program interface. */ @@ -19,22 +19,20 @@ int riscv_program_init(struct riscv_program *p, struct target *target) memset(p, 0, sizeof(*p)); p->target = target; p->instruction_count = 0; - p->target_xlen = riscv_xlen(target); - for (size_t i = 0; i < RISCV_REGISTER_COUNT; ++i) - p->writes_xreg[i] = 0; - for (size_t i = 0; i < RISCV_MAX_DEBUG_BUFFER_SIZE; ++i) - p->debug_buffer[i] = -1; + for (unsigned int i = 0; i < RISCV013_MAX_PROGBUF_SIZE; ++i) + p->progbuf[i] = (uint32_t)(-1); + p->execution_result = RISCV_PROGBUF_EXEC_RESULT_NOT_EXECUTED; return ERROR_OK; } int riscv_program_write(struct riscv_program *program) { for (unsigned int i = 0; i < program->instruction_count; ++i) { - LOG_DEBUG("debug_buffer[%02x] = DASM(0x%08x)", i, program->debug_buffer[i]); - if (riscv_write_debug_buffer(program->target, i, - program->debug_buffer[i]) != ERROR_OK) + LOG_TARGET_DEBUG(program->target, "progbuf[%02x] = DASM(0x%08x)", + i, program->progbuf[i]); + if (riscv_write_progbuf(program->target, i, program->progbuf[i]) != ERROR_OK) return ERROR_FAIL; } return ERROR_OK; @@ -45,90 +43,114 @@ int riscv_program_exec(struct riscv_program *p, struct target *t) { keep_alive(); - riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1]; - for (size_t i = GDB_REGNO_ZERO + 1; i <= GDB_REGNO_XPR31; ++i) { - if (p->writes_xreg[i]) { - LOG_DEBUG("Saving register %d as used by program", (int)i); - int result = riscv_get_register(t, &saved_registers[i], i); - if (result != ERROR_OK) - return result; - } - } + p->execution_result = RISCV_PROGBUF_EXEC_RESULT_UNKNOWN; if (riscv_program_ebreak(p) != ERROR_OK) { - LOG_ERROR("Unable to write ebreak"); - for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i) - LOG_ERROR("ram[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]", - (int)i, p->debug_buffer[i], p->debug_buffer[i]); + LOG_TARGET_ERROR(t, "Unable to insert ebreak into program buffer"); + for (unsigned int i = 0; i < riscv_progbuf_size(p->target); ++i) + LOG_TARGET_ERROR(t, "progbuf[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]", + i, p->progbuf[i], p->progbuf[i]); return ERROR_FAIL; } if (riscv_program_write(p) != ERROR_OK) return ERROR_FAIL; - if (riscv_execute_debug_buffer(t) != ERROR_OK) { - LOG_DEBUG("Unable to execute program %p", p); + uint32_t cmderr; + if (riscv_execute_progbuf(t, &cmderr) != ERROR_OK) { + p->execution_result = (cmderr == DM_ABSTRACTCS_CMDERR_EXCEPTION) + ? RISCV_PROGBUF_EXEC_RESULT_EXCEPTION + : RISCV_PROGBUF_EXEC_RESULT_UNKNOWN_ERROR; + /* TODO: what happens if we fail here, but need to restore registers? */ + LOG_TARGET_DEBUG(t, "Unable to execute program %p", p); return ERROR_FAIL; } - - for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i) - if (i >= riscv_debug_buffer_size(p->target)) - p->debug_buffer[i] = riscv_read_debug_buffer(t, i); - - for (size_t i = GDB_REGNO_ZERO; i <= GDB_REGNO_XPR31; ++i) - if (p->writes_xreg[i]) - riscv_set_register(t, i, saved_registers[i]); + p->execution_result = RISCV_PROGBUF_EXEC_RESULT_SUCCESS; return ERROR_OK; } -int riscv_program_sdr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) +int riscv_program_sdr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset) { return riscv_program_insert(p, sd(d, b, offset)); } -int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) +int riscv_program_swr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset) { return riscv_program_insert(p, sw(d, b, offset)); } -int riscv_program_shr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) +int riscv_program_shr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset) { return riscv_program_insert(p, sh(d, b, offset)); } -int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) +int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset) { return riscv_program_insert(p, sb(d, b, offset)); } -int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) +int riscv_program_store(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset, + unsigned int size) +{ + switch (size) { + case 1: + return riscv_program_sbr(p, d, b, offset); + case 2: + return riscv_program_shr(p, d, b, offset); + case 4: + return riscv_program_swr(p, d, b, offset); + case 8: + return riscv_program_sdr(p, d, b, offset); + } + assert(false && "Unsupported size"); + return ERROR_FAIL; +} + +int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset) { return riscv_program_insert(p, ld(d, b, offset)); } -int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) +int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset) { return riscv_program_insert(p, lw(d, b, offset)); } -int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) +int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset) { return riscv_program_insert(p, lh(d, b, offset)); } -int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset) +int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset) { return riscv_program_insert(p, lb(d, b, offset)); } -int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr) +int riscv_program_load(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t offset, + unsigned int size) +{ + switch (size) { + case 1: + return riscv_program_lbr(p, d, b, offset); + case 2: + return riscv_program_lhr(p, d, b, offset); + case 4: + return riscv_program_lwr(p, d, b, offset); + case 8: + return riscv_program_ldr(p, d, b, offset); + } + assert(false && "Unsupported size"); + return ERROR_FAIL; +} + +int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, uint8_t z, enum gdb_regno csr) { assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095); return riscv_program_insert(p, csrrsi(d, z, csr - GDB_REGNO_CSR0)); } -int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr) +int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, uint8_t z, enum gdb_regno csr) { assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095); return riscv_program_insert(p, csrrci(d, z, csr - GDB_REGNO_CSR0)); @@ -142,7 +164,7 @@ int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr) { - assert(csr >= GDB_REGNO_CSR0); + assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095); return riscv_program_insert(p, csrrw(GDB_REGNO_ZERO, s, csr - GDB_REGNO_CSR0)); } @@ -151,17 +173,17 @@ int riscv_program_fence_i(struct riscv_program *p) return riscv_program_insert(p, fence_i()); } -int riscv_program_fence(struct riscv_program *p) +int riscv_program_fence_rw_rw(struct riscv_program *p) { - return riscv_program_insert(p, fence()); + return riscv_program_insert(p, fence_rw_rw()); } int riscv_program_ebreak(struct riscv_program *p) { struct target *target = p->target; RISCV_INFO(r); - if (p->instruction_count == riscv_debug_buffer_size(p->target) && - r->impebreak) { + if (p->instruction_count == riscv_progbuf_size(target) && + r->get_impebreak(target)) { return ERROR_OK; } return riscv_program_insert(p, ebreak()); @@ -174,14 +196,14 @@ int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno int riscv_program_insert(struct riscv_program *p, riscv_insn_t i) { - if (p->instruction_count >= riscv_debug_buffer_size(p->target)) { - LOG_ERROR("Unable to insert instruction:"); - LOG_ERROR(" instruction_count=%d", (int)p->instruction_count); - LOG_ERROR(" buffer size =%d", (int)riscv_debug_buffer_size(p->target)); + if (p->instruction_count >= riscv_progbuf_size(p->target)) { + LOG_TARGET_ERROR(p->target, "Unable to insert program into progbuf, " + "capacity would be exceeded (progbufsize=%u).", + riscv_progbuf_size(p->target)); return ERROR_FAIL; } - p->debug_buffer[p->instruction_count] = i; + p->progbuf[p->instruction_count] = i; p->instruction_count++; return ERROR_OK; } diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h index 62a04f093..d4b9b970b 100644 --- a/src/target/riscv/program.h +++ b/src/target/riscv/program.h @@ -1,13 +1,19 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef TARGET__RISCV__PROGRAM_H -#define TARGET__RISCV__PROGRAM_H +#ifndef OPENOCD_TARGET_RISCV_PROGRAM_H +#define OPENOCD_TARGET_RISCV_PROGRAM_H #include "riscv.h" -#define RISCV_MAX_DEBUG_BUFFER_SIZE 32 -#define RISCV_REGISTER_COUNT 32 -#define RISCV_DSCRATCH_COUNT 2 +#define RISCV013_MAX_PROGBUF_SIZE 16 + +enum riscv_progbuf_exec_result { + RISCV_PROGBUF_EXEC_RESULT_NOT_EXECUTED, + RISCV_PROGBUF_EXEC_RESULT_UNKNOWN, + RISCV_PROGBUF_EXEC_RESULT_EXCEPTION, + RISCV_PROGBUF_EXEC_RESULT_UNKNOWN_ERROR, + RISCV_PROGBUF_EXEC_RESULT_SUCCESS +}; /* The various RISC-V debug specifications all revolve around setting up * program buffers and executing them on the target. This structure contains a @@ -15,17 +21,14 @@ struct riscv_program { struct target *target; - uint32_t debug_buffer[RISCV_MAX_DEBUG_BUFFER_SIZE]; + uint32_t progbuf[RISCV013_MAX_PROGBUF_SIZE]; /* Number of 32-bit instructions in the program. */ - size_t instruction_count; + unsigned int instruction_count; - /* Side effects of executing this program. These must be accounted for - * in order to maintain correct executing of the target system. */ - bool writes_xreg[RISCV_REGISTER_COUNT]; - - /* XLEN on the target. */ - int target_xlen; + /* execution result of the program */ + /* TODO: remove this field. We should make it a parameter to riscv_program_exec */ + enum riscv_progbuf_exec_result execution_result; }; /* Initializes a program with the header. */ @@ -36,7 +39,7 @@ int riscv_program_write(struct riscv_program *program); /* Executes a program, returning 0 if the program successfully executed. Note * that this may cause registers to be saved or restored, which could result to - * calls to things like riscv_save_register which itself could require a + * calls to things like riscv013_reg_save which itself could require a * program to execute. That's OK, just make sure this eventually terminates. * */ int riscv_program_exec(struct riscv_program *p, struct target *t); @@ -47,25 +50,29 @@ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i); /* Helpers to assemble various instructions. Return 0 on success. These might * assemble into a multi-instruction sequence that overwrites some other * register, but those will be properly saved and restored. */ -int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); -int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); -int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); -int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); +int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int16_t o); +int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int16_t o); +int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int16_t o); +int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int16_t o); +int riscv_program_load(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t o, + unsigned int s); -int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); -int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); -int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); -int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o); +int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int16_t o); +int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int16_t o); +int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int16_t o); +int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int16_t o); +int riscv_program_store(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int16_t o, + unsigned int s); -int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr); -int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr); +int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, uint8_t z, enum gdb_regno csr); +int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, uint8_t z, enum gdb_regno csr); int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr); int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr); int riscv_program_fence_i(struct riscv_program *p); -int riscv_program_fence(struct riscv_program *p); +int riscv_program_fence_rw_rw(struct riscv_program *p); int riscv_program_ebreak(struct riscv_program *p); int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t i); -#endif +#endif /* OPENOCD_TARGET_RISCV_PROGRAM_H */ diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c index f4a7d0e5c..69ad5fcbf 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -13,6 +13,8 @@ #include "config.h" #endif +#include "riscv-011.h" + #include "target/target.h" #include "target/algorithm.h" #include "target/target_type.h" @@ -22,8 +24,10 @@ #include "target/breakpoints.h" #include "helper/time_support.h" #include "riscv.h" -#include "asm.h" +#include "riscv_reg.h" +#include "riscv-011_reg.h" #include "gdb_regs.h" +#include "field_helpers.h" /** * Since almost everything can be accomplish by scanning the dbus register, all @@ -67,8 +71,7 @@ * to the target. Afterwards use cache_get... to read results. */ -#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) -#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) +static int handle_halt(struct target *target, bool announce); /* Constants for legacy SiFive hardware breakpoints. */ #define CSR_BPCONTROL_X (1<<0) @@ -161,15 +164,6 @@ typedef enum slot { #define DRAM_CACHE_SIZE 16 -struct trigger { - uint64_t address; - uint32_t length; - uint64_t mask; - uint64_t value; - bool read, write, execute; - int unique_id; -}; - struct memory_cache_line { uint32_t data; bool valid; @@ -219,7 +213,6 @@ typedef struct { static int poll_target(struct target *target, bool announce); static int riscv011_poll(struct target *target); -static int get_register(struct target *target, riscv_reg_t *value, int regid); /*** Utility functions. ***/ @@ -257,18 +250,46 @@ static unsigned int slot_offset(const struct target *target, slot_t slot) return 0; /* Silence -Werror=return-type */ } +static uint32_t load(const struct target *target, unsigned int rd, + unsigned int base, int16_t offset) +{ + switch (riscv_xlen(target)) { + case 32: + return lw(rd, base, offset); + case 64: + return ld(rd, base, offset); + } + assert(0); + return 0; /* Silence -Werror=return-type */ +} + +static uint32_t store(const struct target *target, unsigned int src, + unsigned int base, int16_t offset) +{ + switch (riscv_xlen(target)) { + case 32: + return sw(src, base, offset); + case 64: + return sd(src, base, offset); + } + assert(0); + return 0; /* Silence -Werror=return-type */ +} + static uint32_t load_slot(const struct target *target, unsigned int dest, slot_t slot) { unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot); - return load(target, dest, ZERO, offset); + assert(offset <= MAX_INT12); + return load(target, dest, ZERO, (int16_t)offset); } static uint32_t store_slot(const struct target *target, unsigned int src, slot_t slot) { unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot); - return store(target, src, ZERO, offset); + assert(offset <= MAX_INT12); + return store(target, src, ZERO, (int16_t)offset); } static uint16_t dram_address(unsigned int index) @@ -279,36 +300,6 @@ static uint16_t dram_address(unsigned int index) return 0x40 + index - 0x10; } -static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) -{ - struct scan_field field; - uint8_t in_value[4]; - uint8_t out_value[4] = { 0 }; - - buf_set_u32(out_value, 0, 32, out); - - jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE); - - field.num_bits = 32; - field.out_value = out_value; - field.in_value = in_value; - jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); - - /* Always return to dbus. */ - jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); - - int retval = jtag_execute_queue(); - if (retval != ERROR_OK) { - LOG_ERROR("failed jtag scan: %d", retval); - return retval; - } - - uint32_t in = buf_get_u32(field.in_value, 0, 32); - LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in); - - return in; -} - static uint32_t idcode_scan(struct target *target) { struct scan_field field; @@ -344,7 +335,7 @@ static void increase_dbus_busy_delay(struct target *target) info->dtmcontrol_idle, info->dbus_busy_delay, info->interrupt_high_delay); - dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET); + dtmcs_scan(target->tap, DTMCONTROL_DBUS_RESET, NULL /* discard value */); } static void increase_interrupt_high_delay(struct target *target) @@ -431,8 +422,13 @@ static dbus_status_t dbus_scan(struct target *target, uint16_t *address_in, .out_value = out, .in_value = in }; + if (address_in) + *address_in = 0; - assert(info->addrbits != 0); + if (info->addrbits == 0) { + LOG_TARGET_ERROR(target, "Can't access DMI because addrbits=0."); + return DBUS_STATUS_FAILED; + } buf_set_u64(out, DBUS_OP_START, DBUS_OP_SIZE, op); buf_set_u64(out, DBUS_DATA_START, DBUS_DATA_SIZE, data_out); @@ -605,9 +601,9 @@ static void scans_add_write32(scans_t *scans, uint16_t address, uint32_t data, static void scans_add_write_jump(scans_t *scans, uint16_t address, bool set_interrupt) { - scans_add_write32(scans, address, - jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*address))), - set_interrupt); + unsigned int jump_offset = DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4 * address); + assert(jump_offset <= MAX_INT21); + scans_add_write32(scans, address, jal(0, (int32_t)jump_offset), set_interrupt); } /** Add a 32-bit dbus write for an instruction that loads from the indicated @@ -686,18 +682,13 @@ static void dram_write32(struct target *target, unsigned int index, uint32_t val } /** Read the haltnot and interrupt bits. */ -static bits_t read_bits(struct target *target) +static int read_bits(struct target *target, bits_t *result) { uint64_t value; dbus_status_t status; uint16_t address_in; riscv011_info_t *info = get_info(target); - bits_t err_result = { - .haltnot = 0, - .interrupt = 0 - }; - do { unsigned int i = 0; do { @@ -706,26 +697,23 @@ static bits_t read_bits(struct target *target) if (address_in == (1<addrbits) - 1 && value == (1ULL<= 256) { + if (status != DBUS_STATUS_SUCCESS) { LOG_ERROR("Failed to read from 0x%x; status=%d", address_in, status); - return err_result; + return ERROR_FAIL; } } while (address_in > 0x10 && address_in != DMCONTROL); - bits_t result = { - .haltnot = get_field(value, DMCONTROL_HALTNOT), - .interrupt = get_field(value, DMCONTROL_INTERRUPT) - }; - return result; + if (result) { + result->haltnot = get_field(value, DMCONTROL_HALTNOT); + result->interrupt = get_field(value, DMCONTROL_INTERRUPT); + } + return ERROR_OK; } static int wait_for_debugint_clear(struct target *target, bool ignore_first) @@ -736,13 +724,19 @@ static int wait_for_debugint_clear(struct target *target, bool ignore_first) * result of the read that happened just before debugint was set. * (Assuming the last scan before calling this function was one that * sets debugint.) */ - read_bits(target); + read_bits(target, NULL); } while (1) { - bits_t bits = read_bits(target); + bits_t bits = { + .haltnot = 0, + .interrupt = 0 + }; + if (read_bits(target, &bits) != ERROR_OK) + return ERROR_FAIL; + if (!bits.interrupt) return ERROR_OK; - if (time(NULL) - start > riscv_command_timeout_sec) { + if (time(NULL) - start > riscv_get_command_timeout_sec()) { LOG_ERROR("Timed out waiting for debug int to clear." "Increase timeout with riscv set_command_timeout_sec."); return ERROR_FAIL; @@ -788,22 +782,25 @@ static void cache_set(struct target *target, slot_t slot, uint64_t data) static void cache_set_jump(struct target *target, unsigned int index) { - cache_set32(target, index, - jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index)))); + unsigned int jump_offset = DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4 * index); + assert(jump_offset <= MAX_INT21); + cache_set32(target, index, jal(0, (int32_t)jump_offset)); } static void cache_set_load(struct target *target, unsigned int index, unsigned int reg, slot_t slot) { - uint16_t offset = DEBUG_RAM_START + 4 * slot_offset(target, slot); - cache_set32(target, index, load(target, reg, ZERO, offset)); + unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot); + assert(offset <= MAX_INT12); + cache_set32(target, index, load(target, reg, ZERO, (int16_t)offset)); } static void cache_set_store(struct target *target, unsigned int index, unsigned int reg, slot_t slot) { - uint16_t offset = DEBUG_RAM_START + 4 * slot_offset(target, slot); - cache_set32(target, index, store(target, reg, ZERO, offset)); + unsigned int offset = DEBUG_RAM_START + 4 * slot_offset(target, slot); + assert(offset <= MAX_INT12); + cache_set32(target, index, store(target, reg, ZERO, (int16_t)offset)); } static void dump_debug_ram(struct target *target) @@ -1012,9 +1009,9 @@ static uint64_t cache_get(struct target *target, slot_t slot) static void dram_write_jump(struct target *target, unsigned int index, bool set_interrupt) { - dram_write32(target, index, - jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index))), - set_interrupt); + unsigned int jump_offset = DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4 * index); + assert(jump_offset <= MAX_INT21); + dram_write32(target, index, jal(0, (int32_t)jump_offset), set_interrupt); } static int wait_for_state(struct target *target, enum target_state state) @@ -1026,7 +1023,7 @@ static int wait_for_state(struct target *target, enum target_state state) return result; if (target->state == state) return ERROR_OK; - if (time(NULL) - start > riscv_command_timeout_sec) { + if (time(NULL) - start > riscv_get_command_timeout_sec()) { LOG_ERROR("Timed out waiting for state %d. " "Increase timeout with riscv set_command_timeout_sec.", state); return ERROR_FAIL; @@ -1048,7 +1045,7 @@ static int read_remote_csr(struct target *target, uint64_t *value, uint32_t csr) uint32_t exception = cache_get32(target, info->dramsize-1); if (exception) { LOG_WARNING("Got exception 0x%x when reading %s", exception, - gdb_regno_name(GDB_REGNO_CSR0 + csr)); + riscv_reg_gdb_regno_name(target, GDB_REGNO_CSR0 + csr)); *value = ~0; return ERROR_FAIL; } @@ -1107,12 +1104,25 @@ static int maybe_write_tselect(struct target *target) return ERROR_OK; } +static uint64_t set_ebreakx_fields(uint64_t dcsr, const struct target *target) +{ + const struct riscv_private_config * const config = riscv_private_config(target); + dcsr = set_field(dcsr, DCSR_EBREAKM, config->dcsr_ebreak_fields[RISCV_MODE_M]); + dcsr = set_field(dcsr, DCSR_EBREAKS, config->dcsr_ebreak_fields[RISCV_MODE_S]); + dcsr = set_field(dcsr, DCSR_EBREAKU, config->dcsr_ebreak_fields[RISCV_MODE_U]); + dcsr = set_field(dcsr, DCSR_EBREAKH, 1); + return dcsr; +} + static int execute_resume(struct target *target, bool step) { riscv011_info_t *info = get_info(target); LOG_DEBUG("step=%d", step); + if (riscv_reg_flush_all(target) != ERROR_OK) + return ERROR_FAIL; + maybe_write_tselect(target); /* TODO: check if dpc is dirty (which also is true if an exception was hit @@ -1137,10 +1147,7 @@ static int execute_resume(struct target *target, bool step) } } - info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1); + info->dcsr = set_ebreakx_fields(info->dcsr, target); info->dcsr &= ~DCSR_HALT; if (step) @@ -1165,7 +1172,7 @@ static int execute_resume(struct target *target, bool step) } target->state = TARGET_RUNNING; - register_cache_invalidate(target->reg_cache); + riscv_reg_cache_invalidate_all(target); return ERROR_OK; } @@ -1183,23 +1190,13 @@ static int full_step(struct target *target, bool announce) return result; if (target->state != TARGET_DEBUG_RUNNING) break; - if (time(NULL) - start > riscv_command_timeout_sec) { + if (time(NULL) - start > riscv_get_command_timeout_sec()) { LOG_ERROR("Timed out waiting for step to complete." "Increase timeout with riscv set_command_timeout_sec"); return ERROR_FAIL; } } - return ERROR_OK; -} - -static int resume(struct target *target, bool debug_execution, bool step) -{ - if (debug_execution) { - LOG_ERROR("TODO: debug_execution is true"); - return ERROR_FAIL; - } - - return execute_resume(target, step); + return handle_halt(target, announce); } static uint64_t reg_cache_get(struct target *target, unsigned int number) @@ -1234,7 +1231,7 @@ static int update_mstatus_actual(struct target *target) /* Force reading the register. In that process mstatus_actual will be * updated. */ riscv_reg_t mstatus; - return get_register(target, &mstatus, GDB_REGNO_MSTATUS); + return riscv011_get_register(target, &mstatus, GDB_REGNO_MSTATUS); } /*** OpenOCD target functions. ***/ @@ -1256,7 +1253,7 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum) uint32_t exception = cache_get32(target, info->dramsize-1); if (exception) { - LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(regnum)); + LOG_WARNING("Got exception 0x%x when reading %s", exception, riscv_reg_gdb_regno_name(target, regnum)); *value = ~0; return ERROR_FAIL; } @@ -1270,7 +1267,7 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum) return ERROR_OK; } -/* Write the register. No caching or games. */ +/* Write the register. */ static int register_write(struct target *target, unsigned int number, uint64_t value) { @@ -1289,7 +1286,7 @@ static int register_write(struct target *target, unsigned int number, } else if (number <= GDB_REGNO_XPR31) { cache_set_load(target, 0, number - GDB_REGNO_ZERO, SLOT0); cache_set_jump(target, 1); - } else if (number == GDB_REGNO_PC) { + } else if (number == GDB_REGNO_PC || number == GDB_REGNO_DPC) { info->dpc = value; return ERROR_OK; } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { @@ -1331,22 +1328,27 @@ static int register_write(struct target *target, unsigned int number, uint32_t exception = cache_get32(target, info->dramsize-1); if (exception) { LOG_WARNING("Got exception 0x%x when writing %s", exception, - gdb_regno_name(number)); + riscv_reg_gdb_regno_name(target, number)); return ERROR_FAIL; } return ERROR_OK; } -static int get_register(struct target *target, riscv_reg_t *value, int regid) +int riscv011_get_register(struct target *target, riscv_reg_t *value, + enum gdb_regno regid) { riscv011_info_t *info = get_info(target); maybe_write_tselect(target); if (regid <= GDB_REGNO_XPR31) { + /* FIXME: Here the implementation assumes that the value + * written to GPR will be the same as the value read back. This + * is not true for a write of a non-zero value to x0. + */ *value = reg_cache_get(target, regid); - } else if (regid == GDB_REGNO_PC) { + } else if (regid == GDB_REGNO_PC || regid == GDB_REGNO_DPC) { *value = info->dpc; } else if (regid >= GDB_REGNO_FPR0 && regid <= GDB_REGNO_FPR31) { int result = update_mstatus_actual(target); @@ -1376,15 +1378,32 @@ static int get_register(struct target *target, riscv_reg_t *value, int regid) return result; } - if (regid == GDB_REGNO_MSTATUS) - target->reg_cache->reg_list[regid].valid = true; - return ERROR_OK; } -static int set_register(struct target *target, int regid, uint64_t value) +/* This function is intended to handle accesses to registers through register + * cache. */ +int riscv011_set_register(struct target *target, enum gdb_regno regid, + riscv_reg_t value) { - return register_write(target, regid, value); + assert(target->reg_cache); + assert(target->reg_cache->reg_list); + struct reg * const reg = &target->reg_cache->reg_list[regid]; + assert(reg); + /* On RISC-V 0.11 targets valid value of some registers (e.g. `dcsr`) + * is stored in `riscv011_info_t` itself, not in register cache. This + * complicates register cache implementation. + * Therefore, for now, caching registers in register cache is disabled + * for all registers, except for reads of GPRs. + */ + assert(!reg->dirty); + int result = register_write(target, regid, value); + if (result != ERROR_OK) + return result; + reg_cache_set(target, regid, value); + /* FIXME: x0 (zero) should not be cached on writes. */ + reg->valid = regid <= GDB_REGNO_XPR31; + return ERROR_OK; } static int halt(struct target *target) @@ -1468,19 +1487,20 @@ static int step(struct target *target, bool current, target_addr_t address, static int examine(struct target *target) { /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ - - uint32_t dtmcontrol = dtmcontrol_scan(target, 0); - LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); - LOG_DEBUG(" addrbits=%d", get_field(dtmcontrol, DTMCONTROL_ADDRBITS)); - LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTMCONTROL_VERSION)); - LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTMCONTROL_IDLE)); - if (dtmcontrol == 0) { - LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power."); + uint32_t dtmcontrol; + if (dtmcs_scan(target->tap, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) { + LOG_ERROR("Could not scan dtmcontrol. Check JTAG connectivity/board power."); return ERROR_FAIL; } + + LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); + LOG_DEBUG(" addrbits=%d", get_field32(dtmcontrol, DTMCONTROL_ADDRBITS)); + LOG_DEBUG(" version=%d", get_field32(dtmcontrol, DTMCONTROL_VERSION)); + LOG_DEBUG(" idle=%d", get_field32(dtmcontrol, DTMCONTROL_IDLE)); + if (get_field(dtmcontrol, DTMCONTROL_VERSION) != 0) { LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)", - get_field(dtmcontrol, DTMCONTROL_VERSION), dtmcontrol); + get_field32(dtmcontrol, DTMCONTROL_VERSION), dtmcontrol); return ERROR_FAIL; } @@ -1498,22 +1518,22 @@ static int examine(struct target *target) uint32_t dminfo = dbus_read(target, DMINFO); LOG_DEBUG("dminfo: 0x%08x", dminfo); - LOG_DEBUG(" abussize=0x%x", get_field(dminfo, DMINFO_ABUSSIZE)); - LOG_DEBUG(" serialcount=0x%x", get_field(dminfo, DMINFO_SERIALCOUNT)); - LOG_DEBUG(" access128=%d", get_field(dminfo, DMINFO_ACCESS128)); - LOG_DEBUG(" access64=%d", get_field(dminfo, DMINFO_ACCESS64)); - LOG_DEBUG(" access32=%d", get_field(dminfo, DMINFO_ACCESS32)); - LOG_DEBUG(" access16=%d", get_field(dminfo, DMINFO_ACCESS16)); - LOG_DEBUG(" access8=%d", get_field(dminfo, DMINFO_ACCESS8)); - LOG_DEBUG(" dramsize=0x%x", get_field(dminfo, DMINFO_DRAMSIZE)); - LOG_DEBUG(" authenticated=0x%x", get_field(dminfo, DMINFO_AUTHENTICATED)); - LOG_DEBUG(" authbusy=0x%x", get_field(dminfo, DMINFO_AUTHBUSY)); - LOG_DEBUG(" authtype=0x%x", get_field(dminfo, DMINFO_AUTHTYPE)); - LOG_DEBUG(" version=0x%x", get_field(dminfo, DMINFO_VERSION)); + LOG_DEBUG(" abussize=0x%x", get_field32(dminfo, DMINFO_ABUSSIZE)); + LOG_DEBUG(" serialcount=0x%x", get_field32(dminfo, DMINFO_SERIALCOUNT)); + LOG_DEBUG(" access128=%d", get_field32(dminfo, DMINFO_ACCESS128)); + LOG_DEBUG(" access64=%d", get_field32(dminfo, DMINFO_ACCESS64)); + LOG_DEBUG(" access32=%d", get_field32(dminfo, DMINFO_ACCESS32)); + LOG_DEBUG(" access16=%d", get_field32(dminfo, DMINFO_ACCESS16)); + LOG_DEBUG(" access8=%d", get_field32(dminfo, DMINFO_ACCESS8)); + LOG_DEBUG(" dramsize=0x%x", get_field32(dminfo, DMINFO_DRAMSIZE)); + LOG_DEBUG(" authenticated=0x%x", get_field32(dminfo, DMINFO_AUTHENTICATED)); + LOG_DEBUG(" authbusy=0x%x", get_field32(dminfo, DMINFO_AUTHBUSY)); + LOG_DEBUG(" authtype=0x%x", get_field32(dminfo, DMINFO_AUTHTYPE)); + LOG_DEBUG(" version=0x%x", get_field32(dminfo, DMINFO_VERSION)); if (get_field(dminfo, DMINFO_VERSION) != 1) { LOG_ERROR("OpenOCD only supports Debug Module version 1, not %d " - "(dminfo=0x%x)", get_field(dminfo, DMINFO_VERSION), dminfo); + "(dminfo=0x%x)", get_field32(dminfo, DMINFO_VERSION), dminfo); return ERROR_FAIL; } @@ -1581,7 +1601,7 @@ static int examine(struct target *target) } /* Update register list to match discovered XLEN/supported extensions. */ - riscv_init_registers(target); + riscv011_reg_init_all(target); info->never_halted = true; @@ -1590,9 +1610,6 @@ static int examine(struct target *target) return result; target_set_examined(target); - riscv_set_current_hartid(target, 0); - for (size_t i = 0; i < 32; ++i) - reg_cache_set(target, i, -1); LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64, riscv_xlen(target), r->misa); @@ -1778,10 +1795,10 @@ static riscv_error_t handle_halt_routine(struct target *target) reg = S0; break; case 31: - reg = CSR_DPC; + reg = GDB_REGNO_DPC; break; case 32: - reg = CSR_DCSR; + reg = GDB_REGNO_DCSR; break; default: assert(0); @@ -1815,8 +1832,8 @@ static riscv_error_t handle_halt_routine(struct target *target) } /* TODO: get rid of those 2 variables and talk to the cache directly. */ - info->dpc = reg_cache_get(target, CSR_DPC); - info->dcsr = reg_cache_get(target, CSR_DCSR); + info->dpc = reg_cache_get(target, GDB_REGNO_DPC); + info->dcsr = reg_cache_get(target, GDB_REGNO_DCSR); cache_invalidate(target); @@ -1872,8 +1889,16 @@ static int handle_halt(struct target *target, bool announce) if (target->debug_reason == DBG_REASON_BREAKPOINT) { int retval; - if (riscv_semihosting(target, &retval) != 0) - return retval; + /* Hotfix: Don't try to handle semihosting before the target is marked as examined. */ + /* TODO: The code should be rearranged so that: + * - Semihosting is not attempted before the target is examined. + * - When the target is already halted on a semihosting magic sequence + * at the time when OpenOCD connects to it, this semihosting attempt + * gets handled right after the examination. + */ + if (target_was_examined(target)) + if (riscv_semihosting(target, &retval) != SEMIHOSTING_NONE) + return retval; } if (announce) @@ -1905,7 +1930,13 @@ static int poll_target(struct target *target, bool announce) int old_debug_level = debug_level; if (debug_level >= LOG_LVL_DEBUG) debug_level = LOG_LVL_INFO; - bits_t bits = read_bits(target); + bits_t bits = { + .haltnot = 0, + .interrupt = 0 + }; + if (read_bits(target, &bits) != ERROR_OK) + return ERROR_FAIL; + debug_level = old_debug_level; if (bits.haltnot && bits.interrupt) { @@ -1937,7 +1968,7 @@ static int riscv011_resume(struct target *target, bool current, jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); r->prepped = false; - return resume(target, debug_execution, false); + return execute_resume(target, false); } static int assert_reset(struct target *target) @@ -1955,10 +1986,7 @@ static int assert_reset(struct target *target) /* Not sure what we should do when there are multiple cores. * Here just reset the single hart we're talking to. */ - info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku); - info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1); + info->dcsr = set_ebreakx_fields(info->dcsr, target); info->dcsr |= DCSR_HALT; if (target->reset_halt) info->dcsr |= DCSR_NDRESET; @@ -1985,9 +2013,16 @@ static int deassert_reset(struct target *target) return wait_for_state(target, TARGET_RUNNING); } -static int read_memory(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +static int read_memory(struct target *target, const struct riscv_mem_access_args args) { + assert(riscv_mem_access_is_read(args)); + + const target_addr_t address = args.address; + const uint32_t size = args.size; + const uint32_t count = args.count; + const uint32_t increment = args.increment; + uint8_t * const buffer = args.read_buffer; + if (increment != size) { LOG_ERROR("read_memory with custom increment not implemented"); return ERROR_NOT_IMPLEMENTED; @@ -2155,9 +2190,20 @@ static int setup_write_memory(struct target *target, uint32_t size) return ERROR_OK; } -static int write_memory(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) +static int write_memory(struct target *target, const struct riscv_mem_access_args args) { + assert(riscv_mem_access_is_write(args)); + + if (args.increment != args.size) { + LOG_TARGET_ERROR(target, "Write increment size has to be equal to element size"); + return ERROR_NOT_IMPLEMENTED; + } + + const target_addr_t address = args.address; + const uint32_t size = args.size; + const uint32_t count = args.count; + const uint8_t * const buffer = args.write_buffer; + riscv011_info_t *info = get_info(target); jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); @@ -2293,6 +2339,15 @@ error: return ERROR_FAIL; } +static int access_memory(struct target *target, const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_valid(args)); + const bool is_write = riscv_mem_access_is_write(args); + if (is_write) + return write_memory(target, args); + return read_memory(target, args); +} + static int arch_state(struct target *target) { return ERROR_OK; @@ -2325,10 +2380,10 @@ static int wait_for_authbusy(struct target *target) uint32_t dminfo = dbus_read(target, DMINFO); if (!get_field(dminfo, DMINFO_AUTHBUSY)) break; - if (time(NULL) - start > riscv_command_timeout_sec) { + if (time(NULL) - start > riscv_get_command_timeout_sec()) { LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dminfo=0x%x). " "Increase the timeout with riscv set_command_timeout_sec.", - riscv_command_timeout_sec, + riscv_get_command_timeout_sec(), dminfo); return ERROR_FAIL; } @@ -2369,17 +2424,27 @@ static int riscv011_authdata_write(struct target *target, uint32_t value, unsign return ERROR_OK; } +static bool riscv011_get_impebreak(const struct target *target) +{ + return false; +} + +static unsigned int riscv011_get_progbufsize(const struct target *target) +{ + return 0; +} + static int init_target(struct command_context *cmd_ctx, struct target *target) { LOG_DEBUG("init"); RISCV_INFO(generic_info); - generic_info->get_register = get_register; - generic_info->set_register = set_register; - generic_info->read_memory = read_memory; + generic_info->access_memory = access_memory; generic_info->authdata_read = &riscv011_authdata_read; generic_info->authdata_write = &riscv011_authdata_write; generic_info->print_info = &riscv011_print_info; + generic_info->get_impebreak = &riscv011_get_impebreak; + generic_info->get_progbufsize = &riscv011_get_progbufsize; generic_info->version_specific = calloc(1, sizeof(riscv011_info_t)); if (!generic_info->version_specific) @@ -2387,7 +2452,7 @@ static int init_target(struct command_context *cmd_ctx, /* Assume 32-bit until we discover the real value in examine(). */ generic_info->xlen = 32; - riscv_init_registers(target); + riscv011_reg_init_all(target); return ERROR_OK; } @@ -2409,7 +2474,5 @@ struct target_type riscv011_target = { .assert_reset = assert_reset, .deassert_reset = deassert_reset, - .write_memory = write_memory, - .arch_state = arch_state, }; diff --git a/src/target/riscv/riscv-011.h b/src/target/riscv/riscv-011.h new file mode 100644 index 000000000..bbbc1946d --- /dev/null +++ b/src/target/riscv/riscv-011.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_RISCV_011_H +#define OPENOCD_TARGET_RISCV_RISCV_011_H + +#include "riscv.h" +#include "gdb_regs.h" +#include "target/target.h" + +int riscv011_get_register(struct target *target, riscv_reg_t *value, + enum gdb_regno regid); +int riscv011_set_register(struct target *target, enum gdb_regno regid, + riscv_reg_t value); + +#endif /* OPENOCD_TARGET_RISCV_RISCV_011_H */ diff --git a/src/target/riscv/riscv-011_reg.c b/src/target/riscv/riscv-011_reg.c new file mode 100644 index 000000000..9b72918c5 --- /dev/null +++ b/src/target/riscv/riscv-011_reg.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "riscv-011_reg.h" + +#include "riscv_reg_impl.h" +#include "riscv-011.h" + +static int riscv011_reg_get(struct reg *reg) +{ + struct target * const target = riscv_reg_impl_get_target(reg); + riscv_reg_t value; + const int result = riscv011_get_register(target, &value, reg->number); + if (result != ERROR_OK) + return result; + buf_set_u64(reg->value, 0, reg->size, value); + return ERROR_OK; +} + +static int riscv011_reg_set(struct reg *reg, uint8_t *buf) +{ + const riscv_reg_t value = buf_get_u64(buf, 0, reg->size); + struct target * const target = riscv_reg_impl_get_target(reg); + return riscv011_set_register(target, reg->number, value); +} + +static const struct reg_arch_type *riscv011_gdb_regno_reg_type(uint32_t regno) +{ + static const struct reg_arch_type riscv011_reg_type = { + .get = riscv011_reg_get, + .set = riscv011_reg_set + }; + return &riscv011_reg_type; +} + + +int riscv011_reg_init_all(struct target *target) +{ + int res = riscv_reg_impl_init_cache(target); + if (res != ERROR_OK) + return res; + + init_shared_reg_info(target); + + RISCV_INFO(r); + assert(!r->vlenb + && "VLENB discovery is not supported on RISC-V 0.11 targets"); + /* Existence of some registers depends on others. + * E.g. the presence of "v0-31" registers is infered from "vlenb" being + * non-zero. + * Currently, discovery of the following registers is not supported on + * RISC-V 0.11 targets. */ + uint32_t non_discoverable_regs[] = { + GDB_REGNO_VLENB, + GDB_REGNO_MTOPI, + GDB_REGNO_MTOPEI + }; + for (unsigned int i = 0; i < ARRAY_SIZE(non_discoverable_regs); ++i) { + const uint32_t regno = non_discoverable_regs[i]; + res = riscv_reg_impl_init_cache_entry(target, regno, + /*exist*/ false, riscv011_gdb_regno_reg_type(regno)); + if (res != ERROR_OK) + return res; + } + + for (uint32_t regno = 0; regno < target->reg_cache->num_regs; ++regno) { + const struct reg * const reg = riscv_reg_impl_cache_entry(target, regno); + if (riscv_reg_impl_is_initialized(reg)) + continue; + res = riscv_reg_impl_init_cache_entry(target, regno, + riscv_reg_impl_gdb_regno_exist(target, regno), + riscv011_gdb_regno_reg_type(regno)); + if (res != ERROR_OK) + return res; + } + + if (riscv_reg_impl_expose_csrs(target) != ERROR_OK) + return ERROR_FAIL; + + riscv_reg_impl_hide_csrs(target); + + return ERROR_OK; +} diff --git a/src/target/riscv/riscv-011_reg.h b/src/target/riscv/riscv-011_reg.h new file mode 100644 index 000000000..4f7911a88 --- /dev/null +++ b/src/target/riscv/riscv-011_reg.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_RISCV_011_REG_H +#define OPENOCD_TARGET_RISCV_RISCV_011_REG_H + +#include "target/target.h" + +/** + * This file describes additional register cache interface available to the + * RISC-V Debug Specification v0.11 targets. + */ + +/** + * Initialize register cache. After this function all registers can be + * safely accessed via functions described here and in `riscv_reg.h`. + */ +int riscv011_reg_init_all(struct target *target); + +#endif /* OPENOCD_TARGET_RISCV_RISCV_011_REG_H */ diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index bb450ce62..e13225e92 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -16,6 +17,7 @@ #include "target/target.h" #include "target/algorithm.h" #include "target/target_type.h" +#include #include #include "jtag/jtag.h" #include "target/register.h" @@ -23,47 +25,55 @@ #include "helper/time_support.h" #include "helper/list.h" #include "riscv.h" +#include "riscv-013.h" +#include "riscv_reg.h" +#include "riscv-013_reg.h" #include "debug_defines.h" #include "rtos/rtos.h" #include "program.h" -#include "asm.h" #include "batch.h" +#include "debug_reg_printer.h" +#include "field_helpers.h" static int riscv013_on_step_or_resume(struct target *target, bool step); static int riscv013_step_or_resume_current_hart(struct target *target, - bool step, bool use_hasel); -static void riscv013_clear_abstract_error(struct target *target); + bool step); +static int riscv013_clear_abstract_error(struct target *target); /* Implementations of the functions in struct riscv_info. */ -static int riscv013_get_register(struct target *target, - riscv_reg_t *value, int rid); -static int riscv013_set_register(struct target *target, int regid, uint64_t value); -static int riscv013_select_current_hart(struct target *target); +static int dm013_select_hart(struct target *target, int hart_index); static int riscv013_halt_prep(struct target *target); static int riscv013_halt_go(struct target *target); static int riscv013_resume_go(struct target *target); static int riscv013_step_current_hart(struct target *target); -static int riscv013_on_halt(struct target *target); static int riscv013_on_step(struct target *target); static int riscv013_resume_prep(struct target *target); -static bool riscv013_is_halted(struct target *target); static enum riscv_halt_reason riscv013_halt_reason(struct target *target); -static int riscv013_write_debug_buffer(struct target *target, unsigned int index, +static int riscv013_write_progbuf(struct target *target, unsigned int index, riscv_insn_t d); -static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned int index); -static int riscv013_execute_debug_buffer(struct target *target); -static void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d); -static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a); -static int riscv013_dmi_write_u64_bits(struct target *target); -static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf); -static int register_read(struct target *target, uint64_t *value, uint32_t number); -static int register_read_direct(struct target *target, uint64_t *value, uint32_t number); -static int register_write_direct(struct target *target, unsigned int number, - uint64_t value); -static int read_memory(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment); -static int write_memory(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, const uint8_t *buffer); +static riscv_insn_t riscv013_read_progbuf(struct target *target, unsigned int + index); +static int riscv013_invalidate_cached_progbuf(struct target *target); +static int riscv013_execute_progbuf(struct target *target, uint32_t *cmderr); +static void riscv013_fill_dmi_write(const struct target *target, uint8_t *buf, uint32_t a, uint32_t d); +static void riscv013_fill_dmi_read(const struct target *target, uint8_t *buf, uint32_t a); +static unsigned int riscv013_get_dmi_address_bits(const struct target *target); +static void riscv013_fill_dm_nop(const struct target *target, uint8_t *buf); +static unsigned int register_size(struct target *target, enum gdb_regno number); +static int register_read_direct(struct target *target, riscv_reg_t *value, + enum gdb_regno number); +static int register_write_direct(struct target *target, enum gdb_regno number, + riscv_reg_t value); +static int riscv013_access_memory(struct target *target, const struct riscv_mem_access_args args); +static bool riscv013_get_impebreak(const struct target *target); +static unsigned int riscv013_get_progbufsize(const struct target *target); + +enum grouptype { + HALT_GROUP, + RESUME_GROUP +}; +static int set_group(struct target *target, bool *supported, unsigned int group, + enum grouptype grouptype); /** * Since almost everything can be accomplish by scanning the dbus register, all @@ -72,80 +82,62 @@ static int write_memory(struct target *target, target_addr_t address, * currently in IR. They should set IR to dbus explicitly. */ -#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) -#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) - -#define CSR_DCSR_CAUSE_SWBP 1 -#define CSR_DCSR_CAUSE_TRIGGER 2 -#define CSR_DCSR_CAUSE_DEBUGINT 3 -#define CSR_DCSR_CAUSE_STEP 4 -#define CSR_DCSR_CAUSE_HALT 5 -#define CSR_DCSR_CAUSE_GROUP 6 - #define RISCV013_INFO(r) riscv013_info_t *r = get_info(target) /*** JTAG registers. ***/ typedef enum { - DMI_OP_NOP = 0, - DMI_OP_READ = 1, - DMI_OP_WRITE = 2 + DMI_OP_NOP = DTM_DMI_OP_NOP, + DMI_OP_READ = DTM_DMI_OP_READ, + DMI_OP_WRITE = DTM_DMI_OP_WRITE } dmi_op_t; typedef enum { - DMI_STATUS_SUCCESS = 0, - DMI_STATUS_FAILED = 2, - DMI_STATUS_BUSY = 3 + DMI_STATUS_SUCCESS = DTM_DMI_OP_SUCCESS, + DMI_STATUS_FAILED = DTM_DMI_OP_FAILED, + DMI_STATUS_BUSY = DTM_DMI_OP_BUSY } dmi_status_t; -typedef enum slot { - SLOT0, - SLOT1, - SLOT_LAST, -} slot_t; - /*** Debug Bus registers. ***/ -#define CMDERR_NONE 0 -#define CMDERR_BUSY 1 -#define CMDERR_NOT_SUPPORTED 2 -#define CMDERR_EXCEPTION 3 -#define CMDERR_HALT_RESUME 4 -#define CMDERR_OTHER 7 +/* TODO: CMDERR_* defines can removed */ +#define CMDERR_NONE DM_ABSTRACTCS_CMDERR_NONE +#define CMDERR_BUSY DM_ABSTRACTCS_CMDERR_BUSY +#define CMDERR_NOT_SUPPORTED DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED +#define CMDERR_EXCEPTION DM_ABSTRACTCS_CMDERR_EXCEPTION +#define CMDERR_HALT_RESUME DM_ABSTRACTCS_CMDERR_HALT_RESUME +#define CMDERR_OTHER DM_ABSTRACTCS_CMDERR_OTHER -/*** Info about the core being debugged. ***/ - -struct trigger { - uint64_t address; - uint32_t length; - uint64_t mask; - uint64_t value; - bool read, write, execute; - int unique_id; -}; - -typedef enum { - YNM_MAYBE, - YNM_YES, - YNM_NO -} yes_no_maybe_t; +#define HART_INDEX_MULTIPLE -1 +#define HART_INDEX_UNKNOWN -2 typedef struct { struct list_head list; unsigned int abs_chain_position; - + /* The base address to access this DM on DMI */ + uint32_t base; /* The number of harts connected to this DM. */ int hart_count; + /* Indicates we already examined this DM, so don't need to do it again. */ + bool was_examined; /* Indicates we already reset this DM, so don't need to do it again. */ bool was_reset; /* Targets that are connected to this DM. */ struct list_head target_list; - /* The currently selected hartid on this DM. */ + /* Contains the ID of the hart that is currently selected by this DM. + * If multiple harts are selected this is HART_INDEX_MULTIPLE. */ int current_hartid; + bool hasel_supported; /* The program buffer stores executable code. 0 is an illegal instruction, * so we use 0 to mean the cached value is invalid. */ uint32_t progbuf_cache[16]; + + /* Some operations are illegal when an abstract command is running. + * The field is used to track whether the last command timed out, and + * abstractcs.busy may have remained set. In that case we may need to + * re-check the busy state before executing these operations. */ + bool abstract_cmd_maybe_busy; } dm013_info_t; typedef struct { @@ -153,6 +145,66 @@ typedef struct { struct target *target; } target_list_t; +struct ac_cache { + uint32_t *commands; + size_t size; +}; + +static int ac_cache_elem_comparator(const void *p_lhs, const void *p_rhs) +{ + uint32_t lhs = *(const uint32_t *)p_lhs; + uint32_t rhs = *(const uint32_t *)p_rhs; + if (lhs < rhs) + return -1; + if (lhs > rhs) + return 1; + return 0; +} + +static struct ac_cache ac_cache_construct(void) +{ + struct ac_cache cache = { + cache.commands = NULL, + cache.size = 0, + }; + return cache; +} + +static void ac_cache_free(struct ac_cache *cache) +{ + free(cache->commands); + cache->commands = NULL; + cache->size = 0; +} + +static void ac_cache_insert(struct ac_cache *cache, uint32_t command) +{ + assert(cache); + + size_t old_size = cache->size; + size_t new_size = old_size + 1; + size_t entry_size = sizeof(*cache->commands); + + uint32_t *commands = realloc(cache->commands, new_size * entry_size); + if (!commands) { + LOG_ERROR("Reallocation to %zu bytes failed", new_size * entry_size); + return; + } + + commands[old_size] = command; + cache->commands = commands; + cache->size = new_size; + + qsort(cache->commands, cache->size, entry_size, + ac_cache_elem_comparator); +} + +static bool ac_cache_contains(const struct ac_cache *cache, uint32_t command) +{ + return bsearch(&command, cache->commands, cache->size, + sizeof(*cache->commands), ac_cache_elem_comparator); +} + typedef struct { /* The indexed used to address this hart in its DM. */ unsigned int index; @@ -162,11 +214,13 @@ typedef struct { unsigned int datacount; /* Number of words in the Program Buffer. */ unsigned int progbufsize; + /* Hart contains an implicit ebreak at the end of the program buffer. */ + bool impebreak; /* We cache the read-only bits of sbcs here. */ uint32_t sbcs; - yes_no_maybe_t progbuf_writable; + enum yes_no_maybe progbuf_writable; /* We only need the address so that we know the alignment of the buffer. */ riscv_addr_t progbuf_address; @@ -174,33 +228,14 @@ typedef struct { * access. */ unsigned int dtmcs_idle; - /* This value is incremented every time a dbus access comes back as "busy". - * It's used to determine how many run-test/idle cycles to feed the target - * in between accesses. */ - unsigned int dmi_busy_delay; + /* This structure is used to determine how many run-test/idle to use after + * an access of corresponding "riscv_scan_delay_class". + * Values are incremented every time an access results in a busy + * response. + */ + struct riscv_scan_delays learned_delays; - /* Number of run-test/idle cycles to add between consecutive bus master - * reads/writes respectively. */ - unsigned int bus_master_write_delay, bus_master_read_delay; - - /* This value is increased every time we tried to execute two commands - * consecutively, and the second one failed because the previous hadn't - * completed yet. It's used to add extra run-test/idle cycles after - * starting a command, so we don't have to waste time checking for busy to - * go low. */ - unsigned int ac_busy_delay; - - bool abstract_read_csr_supported; - bool abstract_write_csr_supported; - bool abstract_read_fpr_supported; - bool abstract_write_fpr_supported; - - yes_no_maybe_t has_aampostincrement; - - /* When a function returns some error due to a failure indicated by the - * target in cmderr, the caller can look here to see what that error was. - * (Compare with errno.) */ - uint8_t cmderr; + struct ac_cache ac_not_supported_cache; /* Some fields from hartinfo. */ uint8_t datasize; @@ -212,6 +247,16 @@ typedef struct { /* DM that provides access to this target. */ dm013_info_t *dm; + + /* This target was selected using hasel. */ + bool selected; + + /* When false, we need to set dcsr.ebreak*, halting the target if that's + * necessary. */ + bool dcsr_ebreak_is_set; + + /* This hart was placed into a halt group in examine(). */ + bool haltgroup_supported; } riscv013_info_t; static OOCD_LIST_HEAD(dm_list); @@ -240,19 +285,25 @@ static dm013_info_t *get_dm(struct target *target) dm013_info_t *entry; dm013_info_t *dm = NULL; list_for_each_entry(entry, &dm_list, list) { - if (entry->abs_chain_position == abs_chain_position) { + if (entry->abs_chain_position == abs_chain_position + && entry->base == target->dbgbase) { dm = entry; break; } } if (!dm) { - LOG_DEBUG("[%d] Allocating new DM", target->coreid); + LOG_TARGET_DEBUG(target, "Coreid [%d] Allocating new DM", target->coreid); dm = calloc(1, sizeof(dm013_info_t)); if (!dm) return NULL; dm->abs_chain_position = abs_chain_position; - dm->current_hartid = -1; + + /* Safety check for dbgbase */ + assert(target->dbgbase_set || target->dbgbase == 0); + + dm->base = target->dbgbase; + dm->current_hartid = 0; dm->hart_count = -1; INIT_LIST_HEAD(&dm->target_list); list_add(&dm->list, &dm_list); @@ -275,422 +326,253 @@ static dm013_info_t *get_dm(struct target *target) return dm; } -static uint32_t set_hartsel(uint32_t initial, uint32_t index) +static void riscv013_dm_free(struct target *target) { - initial &= ~DM_DMCONTROL_HARTSELLO; - initial &= ~DM_DMCONTROL_HARTSELHI; + RISCV013_INFO(info); + dm013_info_t *dm = info->dm; + if (!dm) + return; - uint32_t index_lo = index & ((1 << DM_DMCONTROL_HARTSELLO_LENGTH) - 1); - initial |= index_lo << DM_DMCONTROL_HARTSELLO_OFFSET; - uint32_t index_hi = index >> DM_DMCONTROL_HARTSELLO_LENGTH; - assert(index_hi < 1 << DM_DMCONTROL_HARTSELHI_LENGTH); - initial |= index_hi << DM_DMCONTROL_HARTSELHI_OFFSET; + target_list_t *target_entry; + list_for_each_entry(target_entry, &dm->target_list, list) { + if (target_entry->target == target) { + list_del(&target_entry->list); + free(target_entry); + break; + } + } + + if (list_empty(&dm->target_list)) { + list_del(&dm->list); + free(dm); + } + info->dm = NULL; +} + +static riscv_debug_reg_ctx_t get_riscv_debug_reg_ctx(const struct target *target) +{ + if (!target_was_examined(target)) { + const riscv_debug_reg_ctx_t default_context = {0}; + return default_context; + } + + RISCV013_INFO(info); + const riscv_debug_reg_ctx_t context = { + .XLEN = { .value = riscv_xlen(target), .is_set = true }, + .DXLEN = { .value = riscv_xlen(target), .is_set = true }, + .abits = { .value = info->abits, .is_set = true }, + }; + return context; +} + +static void log_debug_reg(struct target *target, enum riscv_debug_reg_ordinal reg, + riscv_reg_t value, const char *file, unsigned int line, const char *func) +{ + if (debug_level < LOG_LVL_DEBUG) + return; + const riscv_debug_reg_ctx_t context = get_riscv_debug_reg_ctx(target); + char * const buf = malloc(riscv_debug_reg_to_s(NULL, reg, context, value, RISCV_DEBUG_REG_HIDE_UNNAMED_0) + 1); + if (!buf) { + LOG_ERROR("Unable to allocate memory."); + return; + } + riscv_debug_reg_to_s(buf, reg, context, value, RISCV_DEBUG_REG_HIDE_UNNAMED_0); + log_printf_lf(LOG_LVL_DEBUG, file, line, func, "[%s] %s", target_name(target), buf); + free(buf); +} + +#define LOG_DEBUG_REG(t, r, v) log_debug_reg(t, r##_ORDINAL, v, __FILE__, __LINE__, __func__) + +static uint32_t set_dmcontrol_hartsel(uint32_t initial, int hart_index) +{ + assert(hart_index != HART_INDEX_UNKNOWN); + + if (hart_index >= 0) { + initial = set_field(initial, DM_DMCONTROL_HASEL, DM_DMCONTROL_HASEL_SINGLE); + uint32_t index_lo = hart_index & ((1 << DM_DMCONTROL_HARTSELLO_LENGTH) - 1); + initial = set_field(initial, DM_DMCONTROL_HARTSELLO, index_lo); + uint32_t index_hi = hart_index >> DM_DMCONTROL_HARTSELLO_LENGTH; + assert(index_hi < (1 << DM_DMCONTROL_HARTSELHI_LENGTH)); + initial = set_field(initial, DM_DMCONTROL_HARTSELHI, index_hi); + } else if (hart_index == HART_INDEX_MULTIPLE) { + initial = set_field(initial, DM_DMCONTROL_HASEL, DM_DMCONTROL_HASEL_MULTIPLE); + /* TODO: https://github.com/riscv/riscv-openocd/issues/748 */ + initial = set_field(initial, DM_DMCONTROL_HARTSELLO, 0); + initial = set_field(initial, DM_DMCONTROL_HARTSELHI, 0); + } return initial; } -static void decode_dmi(char *text, unsigned int address, unsigned int data) -{ - static const struct { - unsigned int address; - uint64_t mask; - const char *name; - } description[] = { - { DM_DMCONTROL, DM_DMCONTROL_HALTREQ, "haltreq" }, - { DM_DMCONTROL, DM_DMCONTROL_RESUMEREQ, "resumereq" }, - { DM_DMCONTROL, DM_DMCONTROL_HARTRESET, "hartreset" }, - { DM_DMCONTROL, DM_DMCONTROL_HASEL, "hasel" }, - { DM_DMCONTROL, DM_DMCONTROL_HARTSELHI, "hartselhi" }, - { DM_DMCONTROL, DM_DMCONTROL_HARTSELLO, "hartsello" }, - { DM_DMCONTROL, DM_DMCONTROL_NDMRESET, "ndmreset" }, - { DM_DMCONTROL, DM_DMCONTROL_DMACTIVE, "dmactive" }, - { DM_DMCONTROL, DM_DMCONTROL_ACKHAVERESET, "ackhavereset" }, - - { DM_DMSTATUS, DM_DMSTATUS_IMPEBREAK, "impebreak" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLHAVERESET, "allhavereset" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYHAVERESET, "anyhavereset" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLRESUMEACK, "allresumeack" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYRESUMEACK, "anyresumeack" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLNONEXISTENT, "allnonexistent" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYNONEXISTENT, "anynonexistent" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLUNAVAIL, "allunavail" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYUNAVAIL, "anyunavail" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLRUNNING, "allrunning" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYRUNNING, "anyrunning" }, - { DM_DMSTATUS, DM_DMSTATUS_ALLHALTED, "allhalted" }, - { DM_DMSTATUS, DM_DMSTATUS_ANYHALTED, "anyhalted" }, - { DM_DMSTATUS, DM_DMSTATUS_AUTHENTICATED, "authenticated" }, - { DM_DMSTATUS, DM_DMSTATUS_AUTHBUSY, "authbusy" }, - { DM_DMSTATUS, DM_DMSTATUS_HASRESETHALTREQ, "hasresethaltreq" }, - { DM_DMSTATUS, DM_DMSTATUS_CONFSTRPTRVALID, "confstrptrvalid" }, - { DM_DMSTATUS, DM_DMSTATUS_VERSION, "version" }, - - { DM_ABSTRACTCS, DM_ABSTRACTCS_PROGBUFSIZE, "progbufsize" }, - { DM_ABSTRACTCS, DM_ABSTRACTCS_BUSY, "busy" }, - { DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR, "cmderr" }, - { DM_ABSTRACTCS, DM_ABSTRACTCS_DATACOUNT, "datacount" }, - - { DM_COMMAND, DM_COMMAND_CMDTYPE, "cmdtype" }, - - { DM_SBCS, DM_SBCS_SBVERSION, "sbversion" }, - { DM_SBCS, DM_SBCS_SBBUSYERROR, "sbbusyerror" }, - { DM_SBCS, DM_SBCS_SBBUSY, "sbbusy" }, - { DM_SBCS, DM_SBCS_SBREADONADDR, "sbreadonaddr" }, - { DM_SBCS, DM_SBCS_SBACCESS, "sbaccess" }, - { DM_SBCS, DM_SBCS_SBAUTOINCREMENT, "sbautoincrement" }, - { DM_SBCS, DM_SBCS_SBREADONDATA, "sbreadondata" }, - { DM_SBCS, DM_SBCS_SBERROR, "sberror" }, - { DM_SBCS, DM_SBCS_SBASIZE, "sbasize" }, - { DM_SBCS, DM_SBCS_SBACCESS128, "sbaccess128" }, - { DM_SBCS, DM_SBCS_SBACCESS64, "sbaccess64" }, - { DM_SBCS, DM_SBCS_SBACCESS32, "sbaccess32" }, - { DM_SBCS, DM_SBCS_SBACCESS16, "sbaccess16" }, - { DM_SBCS, DM_SBCS_SBACCESS8, "sbaccess8" }, - }; - - text[0] = 0; - for (unsigned int i = 0; i < ARRAY_SIZE(description); i++) { - if (description[i].address == address) { - uint64_t mask = description[i].mask; - unsigned int value = get_field(data, mask); - if (value) { - if (i > 0) - *(text++) = ' '; - if (mask & (mask >> 1)) { - /* If the field is more than 1 bit wide. */ - sprintf(text, "%s=%d", description[i].name, value); - } else { - strcpy(text, description[i].name); - } - text += strlen(text); - } - } - } -} - -static void dump_field(int idle, const struct scan_field *field) -{ - static const char * const op_string[] = {"-", "r", "w", "?"}; - static const char * const status_string[] = {"+", "?", "F", "b"}; - - if (debug_level < LOG_LVL_DEBUG) - return; - - uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits); - unsigned int out_op = get_field(out, DTM_DMI_OP); - unsigned int out_data = get_field(out, DTM_DMI_DATA); - unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET; - - uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits); - unsigned int in_op = get_field(in, DTM_DMI_OP); - unsigned int in_data = get_field(in, DTM_DMI_DATA); - unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET; - - log_printf_lf(LOG_LVL_DEBUG, - __FILE__, __LINE__, "scan", - "%ub %s %08x @%02x -> %s %08x @%02x; %di", - field->num_bits, op_string[out_op], out_data, out_address, - status_string[in_op], in_data, in_address, idle); - - char out_text[500]; - char in_text[500]; - decode_dmi(out_text, out_address, out_data); - decode_dmi(in_text, in_address, in_data); - if (in_text[0] || out_text[0]) { - log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, "scan", "%s -> %s", - out_text, in_text); - } -} - /*** Utility functions. ***/ -static void select_dmi(struct target *target) +static void select_dmi(struct jtag_tap *tap) { if (bscan_tunnel_ir_width != 0) { - select_dmi_via_bscan(target); + select_dmi_via_bscan(tap); return; } - jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); -} + if (!tap->enabled) + LOG_ERROR("BUG: Target's TAP '%s' is disabled!", jtag_tap_name(tap)); -static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) -{ - struct scan_field field; - uint8_t in_value[4]; - uint8_t out_value[4] = { 0 }; - - if (bscan_tunnel_ir_width != 0) - return dtmcontrol_scan_via_bscan(target, out); - - buf_set_u32(out_value, 0, 32, out); - - jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE); - - field.num_bits = 32; - field.out_value = out_value; - field.in_value = in_value; - jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); - - /* Always return to dmi. */ - select_dmi(target); - - int retval = jtag_execute_queue(); - if (retval != ERROR_OK) { - LOG_ERROR("failed jtag scan: %d", retval); - return retval; - } - - uint32_t in = buf_get_u32(field.in_value, 0, 32); - LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in); - - return in; -} - -static void increase_dmi_busy_delay(struct target *target) -{ - riscv013_info_t *info = get_info(target); - info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1; - LOG_DEBUG("dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", - info->dtmcs_idle, info->dmi_busy_delay, - info->ac_busy_delay); - - dtmcontrol_scan(target, DTM_DTMCS_DMIRESET); -} - -/** - * exec: If this is set, assume the scan results in an execution, so more - * run-test/idle cycles may be required. - */ -static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, - uint32_t *data_in, dmi_op_t op, uint32_t address_out, uint32_t data_out, - bool exec) -{ - riscv013_info_t *info = get_info(target); - RISCV_INFO(r); - unsigned int num_bits = info->abits + DTM_DMI_OP_LENGTH + DTM_DMI_DATA_LENGTH; - size_t num_bytes = (num_bits + 7) / 8; - uint8_t in[num_bytes]; - uint8_t out[num_bytes]; - struct scan_field field = { - .num_bits = num_bits, - .out_value = out, - .in_value = in - }; - riscv_bscan_tunneled_scan_context_t bscan_ctxt; - - if (r->reset_delays_wait >= 0) { - r->reset_delays_wait--; - if (r->reset_delays_wait < 0) { - info->dmi_busy_delay = 0; - info->ac_busy_delay = 0; - } - } - - memset(in, 0, num_bytes); - memset(out, 0, num_bytes); - - assert(info->abits != 0); - - buf_set_u32(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op); - buf_set_u32(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out); - buf_set_u32(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out); - - /* I wanted to place this code in a different function, but the way JTAG command - queueing works in the jtag handling functions, the scan fields either have to be - heap allocated, global/static, or else they need to stay on the stack until - the jtag_execute_queue() call. Heap or static fields in this case doesn't seem - the best fit. Declaring stack based field values in a subsidiary function call wouldn't - work. */ - if (bscan_tunnel_ir_width != 0) { - riscv_add_bscan_tunneled_scan(target, &field, &bscan_ctxt); - } else { - /* Assume dbus is already selected. */ - jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); - } - - int idle_count = info->dmi_busy_delay; - if (exec) - idle_count += info->ac_busy_delay; - - if (idle_count) - jtag_add_runtest(idle_count, TAP_IDLE); - - int retval = jtag_execute_queue(); - if (retval != ERROR_OK) { - LOG_ERROR("dmi_scan failed jtag scan"); - if (data_in) - *data_in = ~0; - return DMI_STATUS_FAILED; - } - - if (bscan_tunnel_ir_width != 0) { - /* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */ - buffer_shr(in, num_bytes, 1); - } - - if (data_in) - *data_in = buf_get_u32(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); - - if (address_in) - *address_in = buf_get_u32(in, DTM_DMI_ADDRESS_OFFSET, info->abits); - dump_field(idle_count, &field); - return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); -} - -/** - * @param target - * @param data_in The data we received from the target. - * @param dmi_busy_encountered - * If non-NULL, will be updated to reflect whether DMI busy was - * encountered while executing this operation or not. - * @param dmi_op The operation to perform (read/write/nop). - * @param address The address argument to that operation. - * @param data_out The data to send to the target. - * @param timeout_sec - * @param exec When true, this scan will execute something, so extra RTI - * cycles may be added. - * @param ensure_success - * Scan a nop after the requested operation, ensuring the - * DMI operation succeeded. - */ -static int dmi_op_timeout(struct target *target, uint32_t *data_in, - bool *dmi_busy_encountered, int dmi_op, uint32_t address, - uint32_t data_out, int timeout_sec, bool exec, bool ensure_success) -{ - select_dmi(target); - - dmi_status_t status; - uint32_t address_in; - - if (dmi_busy_encountered) - *dmi_busy_encountered = false; - - const char *op_name; - switch (dmi_op) { - case DMI_OP_NOP: - op_name = "nop"; - break; - case DMI_OP_READ: - op_name = "read"; - break; - case DMI_OP_WRITE: - op_name = "write"; - break; - default: - LOG_ERROR("Invalid DMI operation: %d", dmi_op); - return ERROR_FAIL; - } - - keep_alive(); - - time_t start = time(NULL); - /* This first loop performs the request. Note that if for some reason this - * stays busy, it is actually due to the previous access. */ - while (1) { - status = dmi_scan(target, NULL, NULL, dmi_op, address, data_out, - exec); - if (status == DMI_STATUS_BUSY) { - increase_dmi_busy_delay(target); - if (dmi_busy_encountered) - *dmi_busy_encountered = true; - } else if (status == DMI_STATUS_SUCCESS) { - break; - } else { - LOG_ERROR("failed %s at 0x%x, status=%d", op_name, address, status); - dtmcontrol_scan(target, DTM_DTMCS_DMIRESET); - return ERROR_FAIL; - } - if (time(NULL) - start > timeout_sec) - return ERROR_TIMEOUT_REACHED; - } - - if (status != DMI_STATUS_SUCCESS) { - LOG_ERROR("Failed %s at 0x%x; status=%d", op_name, address, status); - return ERROR_FAIL; - } - - if (ensure_success) { - /* This second loop ensures the request succeeded, and gets back data. - * Note that NOP can result in a 'busy' result as well, but that would be - * noticed on the next DMI access we do. */ - while (1) { - status = dmi_scan(target, &address_in, data_in, DMI_OP_NOP, address, 0, - false); - if (status == DMI_STATUS_BUSY) { - increase_dmi_busy_delay(target); - if (dmi_busy_encountered) - *dmi_busy_encountered = true; - } else if (status == DMI_STATUS_SUCCESS) { + bool need_ir_scan = false; + /* FIXME: make "tap" a const pointer. */ + for (struct jtag_tap *other_tap = jtag_tap_next_enabled(NULL); + other_tap; other_tap = jtag_tap_next_enabled(other_tap)) { + if (other_tap != tap) { + /* Different TAP than ours - check if it is in bypass */ + if (!other_tap->bypass) { + need_ir_scan = true; + break; + } + } else { + /* Our TAP - check if the correct instruction is already loaded */ + if (!buf_eq(tap->cur_instr, select_dbus.out_value, tap->ir_length)) { + need_ir_scan = true; break; - } else { - if (data_in) { - LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d", - op_name, address, *data_in, status); - } else { - LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address, - status); - } - dtmcontrol_scan(target, DTM_DTMCS_DMIRESET); - return ERROR_FAIL; } - if (time(NULL) - start > timeout_sec) - return ERROR_TIMEOUT_REACHED; } } - return ERROR_OK; + if (need_ir_scan) + jtag_add_ir_scan(tap, &select_dbus, TAP_IDLE); } -static int dmi_op(struct target *target, uint32_t *data_in, - bool *dmi_busy_encountered, int dmi_op, uint32_t address, - uint32_t data_out, bool exec, bool ensure_success) +static int increase_dmi_busy_delay(struct target *target) { - int result = dmi_op_timeout(target, data_in, dmi_busy_encountered, dmi_op, - address, data_out, riscv_command_timeout_sec, exec, ensure_success); - if (result == ERROR_TIMEOUT_REACHED) { - LOG_ERROR("DMI operation didn't complete in %d seconds. The target is " - "either really slow or broken. You could increase the " - "timeout with riscv set_command_timeout_sec.", - riscv_command_timeout_sec); - return ERROR_FAIL; - } - return result; + RISCV013_INFO(info); + + int res = dtmcs_scan(target->tap, DTM_DTMCS_DMIRESET, + NULL /* discard result */); + if (res != ERROR_OK) + return res; + + res = riscv_scan_increase_delay(&info->learned_delays, + RISCV_DELAY_BASE); + return res; } +static void reset_learned_delays(struct target *target) +{ + RISCV013_INFO(info); + assert(info); + memset(&info->learned_delays, 0, sizeof(info->learned_delays)); +} + +static void decrement_reset_delays_counter(struct target *target, size_t finished_scans) +{ + RISCV_INFO(r); + if (r->reset_delays_wait < 0) { + assert(r->reset_delays_wait == -1); + return; + } + if ((size_t)r->reset_delays_wait >= finished_scans) { + r->reset_delays_wait -= finished_scans; + return; + } + r->reset_delays_wait = -1; + LOG_TARGET_DEBUG(target, + "resetting learned delays (reset_delays_wait counter expired)"); + reset_learned_delays(target); +} + +static uint32_t riscv013_get_dmi_address(const struct target *target, uint32_t address) +{ + assert(target); + uint32_t base = 0; + RISCV013_INFO(info); + if (info && info->dm) + base = info->dm->base; + return address + base; +} + +static int batch_run_timeout(struct target *target, struct riscv_batch *batch); + static int dmi_read(struct target *target, uint32_t *value, uint32_t address) { - return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, false, true); + struct riscv_batch *batch = riscv_batch_alloc(target, 1); + riscv_batch_add_dmi_read(batch, address, RISCV_DELAY_BASE); + int res = batch_run_timeout(target, batch); + if (res == ERROR_OK && value) + *value = riscv_batch_get_dmi_read_data(batch, 0); + riscv_batch_free(batch); + return res; } -static int dmi_read_exec(struct target *target, uint32_t *value, uint32_t address) +static int dm_read(struct target *target, uint32_t *value, uint32_t address) { - return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, true, true); + return dmi_read(target, value, riscv013_get_dmi_address(target, address)); +} + +static int dm_read_exec(struct target *target, uint32_t *value, uint32_t address) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + struct riscv_batch *batch = riscv_batch_alloc(target, 1); + riscv_batch_add_dm_read(batch, address, RISCV_DELAY_ABSTRACT_COMMAND); + dm->abstract_cmd_maybe_busy = true; + int res = batch_run_timeout(target, batch); + if (res == ERROR_OK && value) + *value = riscv_batch_get_dmi_read_data(batch, 0); + riscv_batch_free(batch); + return res; } static int dmi_write(struct target *target, uint32_t address, uint32_t value) { - return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, false, true); + struct riscv_batch *batch = riscv_batch_alloc(target, 1); + riscv_batch_add_dmi_write(batch, address, value, /*read_back*/ true, + RISCV_DELAY_BASE); + int res = batch_run_timeout(target, batch); + riscv_batch_free(batch); + return res; } -static int dmi_write_exec(struct target *target, uint32_t address, - uint32_t value, bool ensure_success) +static int dm_write(struct target *target, uint32_t address, uint32_t value) { - return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, true, ensure_success); + return dmi_write(target, riscv013_get_dmi_address(target, address), value); } -static int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus, - bool authenticated, unsigned int timeout_sec) +static bool check_dbgbase_exists(struct target *target) { - int result = dmi_op_timeout(target, dmstatus, NULL, DMI_OP_READ, - DM_DMSTATUS, 0, timeout_sec, false, true); + uint32_t next_dm = 0; + unsigned int count = 1; + + LOG_TARGET_DEBUG(target, "Searching for DM with DMI base address (dbgbase) = 0x%x", target->dbgbase); + while (1) { + uint32_t current_dm = next_dm; + if (current_dm == target->dbgbase) + return true; + if (dmi_read(target, &next_dm, DM_NEXTDM + current_dm) != ERROR_OK) + break; + LOG_TARGET_DEBUG(target, "dm @ 0x%x --> nextdm=0x%x", current_dm, next_dm); + /* Check if it's last one in the chain. */ + if (next_dm == 0) { + LOG_TARGET_ERROR(target, "Reached the end of DM chain (detected %u DMs in total).", count); + break; + } + /* Safety: Avoid looping forever in case of buggy nextdm values in the hardware. */ + if (count++ > RISCV_MAX_DMS) { + LOG_TARGET_ERROR(target, "Supporting no more than %d DMs on a DMI bus. Aborting", RISCV_MAX_DMS); + break; + } + } + return false; +} + +static int dmstatus_read(struct target *target, uint32_t *dmstatus, + bool authenticated) +{ + int result = dm_read(target, dmstatus, DM_DMSTATUS); if (result != ERROR_OK) return result; int dmstatus_version = get_field(*dmstatus, DM_DMSTATUS_VERSION); if (dmstatus_version != 2 && dmstatus_version != 3) { LOG_ERROR("OpenOCD only supports Debug Module version 2 (0.13) and 3 (1.0), not " - "%d (dmstatus=0x%x). This error might be caused by a JTAG " + "%" PRId32 " (dmstatus=0x%" PRIx32 "). This error might be caused by a JTAG " "signal issue. Try reducing the JTAG clock speed.", - get_field(*dmstatus, DM_DMSTATUS_VERSION), *dmstatus); + get_field32(*dmstatus, DM_DMSTATUS_VERSION), *dmstatus); } else if (authenticated && !get_field(*dmstatus, DM_DMSTATUS_AUTHENTICATED)) { LOG_ERROR("Debugger is not authenticated to target Debug Module. " "(dmstatus=0x%x). Use `riscv authdata_read` and " @@ -700,20 +582,11 @@ static int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus, return ERROR_OK; } -static int dmstatus_read(struct target *target, uint32_t *dmstatus, - bool authenticated) -{ - return dmstatus_read_timeout(target, dmstatus, authenticated, - riscv_command_timeout_sec); -} - -static void increase_ac_busy_delay(struct target *target) +static int increase_ac_busy_delay(struct target *target) { riscv013_info_t *info = get_info(target); - info->ac_busy_delay += info->ac_busy_delay / 10 + 1; - LOG_DEBUG("dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d", - info->dtmcs_idle, info->dmi_busy_delay, - info->ac_busy_delay); + return riscv_scan_increase_delay(&info->learned_delays, + RISCV_DELAY_ABSTRACT_COMMAND); } static uint32_t __attribute__((unused)) abstract_register_size(unsigned int width) @@ -733,113 +606,262 @@ static uint32_t __attribute__((unused)) abstract_register_size(unsigned int widt static int wait_for_idle(struct target *target, uint32_t *abstractcs) { - RISCV013_INFO(info); + assert(target); + assert(abstractcs); + + dm013_info_t *dm = get_dm(target); + if (!dm) { + LOG_ERROR("BUG: Target %s is not assigned to any RISC-V debug module", + target_name(target)); + *abstractcs = 0; + return ERROR_FAIL; + } + time_t start = time(NULL); - while (1) { - if (dmi_read(target, abstractcs, DM_ABSTRACTCS) != ERROR_OK) - return ERROR_FAIL; - - if (get_field(*abstractcs, DM_ABSTRACTCS_BUSY) == 0) - return ERROR_OK; - - if (time(NULL) - start > riscv_command_timeout_sec) { - info->cmderr = get_field(*abstractcs, DM_ABSTRACTCS_CMDERR); - if (info->cmderr != CMDERR_NONE) { - const char *errors[8] = { - "none", - "busy", - "not supported", - "exception", - "halt/resume", - "reserved", - "reserved", - "other" }; - - LOG_ERROR("Abstract command ended in error '%s' (abstractcs=0x%x)", - errors[info->cmderr], *abstractcs); - } - - LOG_ERROR("Timed out after %ds waiting for busy to go low (abstractcs=0x%x). " - "Increase the timeout with riscv set_command_timeout_sec.", - riscv_command_timeout_sec, - *abstractcs); + do { + if (dm_read(target, abstractcs, DM_ABSTRACTCS) != ERROR_OK) { + /* We couldn't read abstractcs. For safety, overwrite the output value to + * prevent the caller working with a stale value of abstractcs. */ + *abstractcs = 0; + LOG_TARGET_ERROR(target, + "potentially unrecoverable error detected - could not read abstractcs"); return ERROR_FAIL; } + + if (get_field(*abstractcs, DM_ABSTRACTCS_BUSY) == 0) { + dm->abstract_cmd_maybe_busy = false; + return ERROR_OK; + } + } while ((time(NULL) - start) < riscv_get_command_timeout_sec()); + + LOG_TARGET_ERROR(target, + "Timed out after %ds waiting for busy to go low (abstractcs=0x%" PRIx32 "). " + "Increase the timeout with riscv set_command_timeout_sec.", + riscv_get_command_timeout_sec(), + *abstractcs); + + if (!dm->abstract_cmd_maybe_busy) + LOG_TARGET_ERROR(target, + "BUG: dm->abstract_cmd_maybe_busy had not been set when starting an abstract command."); + dm->abstract_cmd_maybe_busy = true; + + return ERROR_TIMEOUT_REACHED; +} + +static int dm013_select_target(struct target *target) +{ + riscv013_info_t *info = get_info(target); + return dm013_select_hart(target, info->index); +} + +#define ABSTRACT_COMMAND_BATCH_SIZE 2 + +static size_t abstract_cmd_fill_batch(struct riscv_batch *batch, + uint32_t command) +{ + assert(riscv_batch_available_scans(batch) + >= ABSTRACT_COMMAND_BATCH_SIZE); + riscv_batch_add_dm_write(batch, DM_COMMAND, command, /* read_back */ true, + RISCV_DELAY_ABSTRACT_COMMAND); + return riscv_batch_add_dm_read(batch, DM_ABSTRACTCS, RISCV_DELAY_BASE); +} + +static int abstract_cmd_batch_check_and_clear_cmderr(struct target *target, + const struct riscv_batch *batch, size_t abstractcs_read_key, + uint32_t *cmderr) +{ + uint32_t abstractcs = riscv_batch_get_dmi_read_data(batch, + abstractcs_read_key); + int res; + LOG_DEBUG_REG(target, DM_ABSTRACTCS, abstractcs); + if (get_field32(abstractcs, DM_ABSTRACTCS_BUSY) != 0) { + res = wait_for_idle(target, &abstractcs); + if (res != ERROR_OK) + goto clear_cmderr; + res = increase_ac_busy_delay(target); + if (res != ERROR_OK) + goto clear_cmderr; + } + + dm013_info_t * const dm = get_dm(target); + if (!dm) { + LOG_ERROR("BUG: Target %s is not assigned to any RISC-V debug module", + target_name(target)); + return ERROR_FAIL; + } + dm->abstract_cmd_maybe_busy = false; + + *cmderr = get_field32(abstractcs, DM_ABSTRACTCS_CMDERR); + if (*cmderr == CMDERR_NONE) + return ERROR_OK; + res = ERROR_FAIL; + LOG_TARGET_DEBUG(target, + "Abstract Command execution failed (abstractcs.cmderr = %" PRIx32 ").", + *cmderr); +clear_cmderr: + /* Attempt to clear the error. */ + /* TODO: can we add a more substantial recovery if the clear operation fails? */ + if (dm_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR) != ERROR_OK) + LOG_TARGET_ERROR(target, "could not clear abstractcs error"); + return res; +} + +enum riscv_debug_reg_ordinal get_cmdtype(uint32_t command) +{ + switch (get_field(command, DM_COMMAND_CMDTYPE)) { + case 0: + return AC_ACCESS_REGISTER_ORDINAL; + case 1: + return AC_QUICK_ACCESS_ORDINAL; + case 2: + return AC_ACCESS_MEMORY_ORDINAL; + default: + assert(false && "Unknown command type value"); + return 0; } } -static int execute_abstract_command(struct target *target, uint32_t command) +static void mark_command_as_unsupported(struct target *target, uint32_t command) { - RISCV013_INFO(info); + LOG_TARGET_DEBUG(target, "Caching the abstract " + "command 0x%" PRIx32 " as not supported", command); + log_debug_reg(target, get_cmdtype(command), + command, __FILE__, __LINE__, __func__); + ac_cache_insert(&get_info(target)->ac_not_supported_cache, command); +} + +int riscv013_execute_abstract_command(struct target *target, uint32_t command, + uint32_t *cmderr) +{ + assert(cmderr); + *cmderr = CMDERR_NONE; if (debug_level >= LOG_LVL_DEBUG) { switch (get_field(command, DM_COMMAND_CMDTYPE)) { case 0: - LOG_DEBUG("command=0x%x; access register, size=%d, postexec=%d, " - "transfer=%d, write=%d, regno=0x%x", - command, - 8 << get_field(command, AC_ACCESS_REGISTER_AARSIZE), - get_field(command, AC_ACCESS_REGISTER_POSTEXEC), - get_field(command, AC_ACCESS_REGISTER_TRANSFER), - get_field(command, AC_ACCESS_REGISTER_WRITE), - get_field(command, AC_ACCESS_REGISTER_REGNO)); + LOG_DEBUG_REG(target, AC_ACCESS_REGISTER, command); break; default: - LOG_DEBUG("command=0x%x", command); + LOG_TARGET_DEBUG(target, "command=0x%x", command); break; } } - if (dmi_write_exec(target, DM_COMMAND, command, false) != ERROR_OK) + dm013_info_t *dm = get_dm(target); + if (!dm) return ERROR_FAIL; - uint32_t abstractcs = 0; - int result = wait_for_idle(target, &abstractcs); + struct riscv_batch *batch = riscv_batch_alloc(target, + ABSTRACT_COMMAND_BATCH_SIZE); + const size_t abstractcs_read_key = abstract_cmd_fill_batch(batch, command); - info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR); - if (info->cmderr != 0 || result != ERROR_OK) { - LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, abstractcs); - /* Clear the error. */ - dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); - return ERROR_FAIL; - } + /* Abstract commands are executed while running the batch. */ + dm->abstract_cmd_maybe_busy = true; - return ERROR_OK; + int res = batch_run_timeout(target, batch); + if (res != ERROR_OK) + goto cleanup; + + res = abstract_cmd_batch_check_and_clear_cmderr(target, batch, + abstractcs_read_key, cmderr); + if (res != ERROR_OK && *cmderr == CMDERR_NOT_SUPPORTED) + mark_command_as_unsupported(target, command); + +cleanup: + riscv_batch_free(batch); + return res; } -static riscv_reg_t read_abstract_arg(struct target *target, unsigned int index, +/** + * Queue scans into a batch that read the value from abstract data registers: + * data[index] (and data[index+1] in case of 64-bit value). + * + * No extra DTM delay is added after the write to data[N]. It is assumed that + * this is a one-shot abstract command, that means no auto-execution is set up + * (abstractauto.autoexecdata bits are zero). + */ +static void abstract_data_read_fill_batch(struct riscv_batch *batch, unsigned int index, unsigned int size_bits) { + assert(size_bits >= 32); + assert(size_bits % 32 == 0); + const unsigned int size_in_words = size_bits / 32; + const unsigned int offset = index * size_in_words; + for (unsigned int i = 0; i < size_in_words; ++i) { + const unsigned int reg_address = DM_DATA0 + offset + i; + riscv_batch_add_dm_read(batch, reg_address, RISCV_DELAY_BASE); + } +} + +static riscv_reg_t abstract_data_get_from_batch(struct riscv_batch *batch, + unsigned int index, unsigned int size_bits) +{ + assert(size_bits >= 32); + assert(size_bits % 32 == 0); + const unsigned int size_in_words = size_bits / 32; + assert(size_in_words * sizeof(uint32_t) <= sizeof(riscv_reg_t)); riscv_reg_t value = 0; - uint32_t v; - unsigned int offset = index * size_bits / 32; - switch (size_bits) { - default: - LOG_ERROR("Unsupported size: %d bits", size_bits); - return ~0; - case 64: - dmi_read(target, &v, DM_DATA0 + offset + 1); - value |= ((uint64_t) v) << 32; - /* falls through */ - case 32: - dmi_read(target, &v, DM_DATA0 + offset); - value |= v; + for (unsigned int i = 0; i < size_in_words; ++i) { + const uint32_t v = riscv_batch_get_dmi_read_data(batch, i); + value |= ((riscv_reg_t)v) << (i * 32); } return value; } +static int read_abstract_arg(struct target *target, riscv_reg_t *value, + unsigned int index, unsigned int size_bits) +{ + assert(value); + assert(size_bits >= 32); + assert(size_bits % 32 == 0); + const unsigned char size_in_words = size_bits / 32; + struct riscv_batch * const batch = riscv_batch_alloc(target, size_in_words); + abstract_data_read_fill_batch(batch, index, size_bits); + int result = batch_run_timeout(target, batch); + if (result == ERROR_OK) + *value = abstract_data_get_from_batch(batch, index, size_bits); + riscv_batch_free(batch); + return result; +} + +/** + * Queue scans into a batch that write the value to abstract data registers: + * data[index] (and data[index+1] in case of 64-bit value). + * + * No extra DTM delay is added after the write to data[N]. It is assumed that + * this is a one-shot abstract command, that means no auto-execution is set up + * (abstractauto.autoexecdata bits are zero). + */ +static void abstract_data_write_fill_batch(struct riscv_batch *batch, + riscv_reg_t value, unsigned int index, unsigned int size_bits) +{ + assert(size_bits % 32 == 0); + const unsigned int size_in_words = size_bits / 32; + assert(value <= UINT32_MAX || size_in_words > 1); + const unsigned int offset = index * size_in_words; + + for (unsigned int i = 0; i < size_in_words; ++i) { + const unsigned int reg_address = DM_DATA0 + offset + i; + + riscv_batch_add_dm_write(batch, reg_address, (uint32_t)value, + /* read_back */ true, RISCV_DELAY_BASE); + value >>= 32; + } +} + +/* TODO: reuse "abstract_data_write_fill_batch()" here*/ static int write_abstract_arg(struct target *target, unsigned int index, riscv_reg_t value, unsigned int size_bits) { unsigned int offset = index * size_bits / 32; switch (size_bits) { default: - LOG_ERROR("Unsupported size: %d bits", size_bits); + LOG_TARGET_ERROR(target, "Unsupported size: %d bits", size_bits); return ERROR_FAIL; case 64: - dmi_write(target, DM_DATA0 + offset + 1, value >> 32); + dm_write(target, DM_DATA0 + offset + 1, (uint32_t)(value >> 32)); /* falls through */ case 32: - dmi_write(target, DM_DATA0 + offset, value); + dm_write(target, DM_DATA0 + offset, (uint32_t)value); } return ERROR_OK; } @@ -847,7 +869,7 @@ static int write_abstract_arg(struct target *target, unsigned int index, /** * @par size in bits */ -static uint32_t access_register_command(struct target *target, uint32_t number, +uint32_t riscv013_access_register_command(struct target *target, uint32_t number, unsigned int size, uint32_t flags) { uint32_t command = set_field(0, DM_COMMAND_CMDTYPE, 0); @@ -859,8 +881,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number, command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3); break; default: - LOG_ERROR("%d-bit register %s not supported.", size, - gdb_regno_name(number)); + LOG_TARGET_ERROR(target, "%d-bit register %s not supported.", + size, riscv_reg_gdb_regno_name(target, number)); assert(0); } @@ -889,78 +911,89 @@ static uint32_t access_register_command(struct target *target, uint32_t number, return command; } -static int register_read_abstract(struct target *target, uint64_t *value, - uint32_t number, unsigned int size) +static bool is_command_unsupported(struct target *target, uint32_t command) { - RISCV013_INFO(info); + bool unsupported = ac_cache_contains(&get_info(target)->ac_not_supported_cache, command); + if (!unsupported) + return false; - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - !info->abstract_read_fpr_supported) - return ERROR_FAIL; - if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && - !info->abstract_read_csr_supported) - return ERROR_FAIL; + LOG_TARGET_DEBUG(target, "Abstract command 0x%" + PRIx32 " is cached as not supported", command); + log_debug_reg(target, get_cmdtype(command), + command, __FILE__, __LINE__, __func__); + return true; +} + +static int register_read_abstract_with_size(struct target *target, + riscv_reg_t *value, enum gdb_regno number, unsigned int size) +{ /* The spec doesn't define abstract register numbers for vector registers. */ if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) return ERROR_FAIL; - uint32_t command = access_register_command(target, number, size, + uint32_t command = riscv013_access_register_command(target, number, size, AC_ACCESS_REGISTER_TRANSFER); + if (is_command_unsupported(target, command)) + return ERROR_FAIL; - int result = execute_abstract_command(target, command); - if (result != ERROR_OK) { - if (info->cmderr == CMDERR_NOT_SUPPORTED) { - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - info->abstract_read_fpr_supported = false; - LOG_INFO("Disabling abstract command reads from FPRs."); - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - info->abstract_read_csr_supported = false; - LOG_INFO("Disabling abstract command reads from CSRs."); - } - } + uint32_t cmderr; + int result = riscv013_execute_abstract_command(target, command, &cmderr); + if (result != ERROR_OK) return result; - } if (value) - *value = read_abstract_arg(target, 0, size); + return read_abstract_arg(target, value, 0, size); return ERROR_OK; } -static int register_write_abstract(struct target *target, uint32_t number, - uint64_t value, unsigned int size) +static int register_read_abstract(struct target *target, riscv_reg_t *value, + enum gdb_regno number) { - RISCV013_INFO(info); + const unsigned int size = register_size(target, number); - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - !info->abstract_write_fpr_supported) - return ERROR_FAIL; - if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && - !info->abstract_write_csr_supported) + return register_read_abstract_with_size(target, value, number, size); +} + +static int register_write_abstract(struct target *target, enum gdb_regno number, + riscv_reg_t value) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) return ERROR_FAIL; - uint32_t command = access_register_command(target, number, size, + const unsigned int size_bits = register_size(target, number); + const uint32_t command = riscv013_access_register_command(target, number, size_bits, AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_WRITE); - - if (write_abstract_arg(target, 0, value, size) != ERROR_OK) + if (is_command_unsupported(target, command)) return ERROR_FAIL; - int result = execute_abstract_command(target, command); - if (result != ERROR_OK) { - if (info->cmderr == CMDERR_NOT_SUPPORTED) { - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - info->abstract_write_fpr_supported = false; - LOG_INFO("Disabling abstract command writes to FPRs."); - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - info->abstract_write_csr_supported = false; - LOG_INFO("Disabling abstract command writes to CSRs."); - } - } - return result; - } + LOG_DEBUG_REG(target, AC_ACCESS_REGISTER, command); + assert(size_bits % 32 == 0); + const unsigned int size_in_words = size_bits / 32; + const unsigned int batch_size = size_in_words + + ABSTRACT_COMMAND_BATCH_SIZE; + struct riscv_batch * const batch = riscv_batch_alloc(target, batch_size); - return ERROR_OK; + abstract_data_write_fill_batch(batch, value, /*index*/ 0, size_bits); + const size_t abstractcs_read_key = abstract_cmd_fill_batch(batch, command); + /* Abstract commands are executed while running the batch. */ + dm->abstract_cmd_maybe_busy = true; + + int res = batch_run_timeout(target, batch); + if (res != ERROR_OK) + goto cleanup; + + uint32_t cmderr; + res = abstract_cmd_batch_check_and_clear_cmderr(target, batch, + abstractcs_read_key, &cmderr); + if (res != ERROR_OK && cmderr == CMDERR_NOT_SUPPORTED) + mark_command_as_unsupported(target, command); + +cleanup: + riscv_batch_free(batch); + return res; } /* @@ -990,14 +1023,14 @@ static uint32_t abstract_memory_size(unsigned int width) * Creates a memory access abstract command. */ static uint32_t access_memory_command(struct target *target, bool virtual, - unsigned int width, bool postincrement, bool write) + unsigned int width, bool postincrement, bool is_write) { uint32_t command = set_field(0, AC_ACCESS_MEMORY_CMDTYPE, 2); command = set_field(command, AC_ACCESS_MEMORY_AAMVIRTUAL, virtual); command |= abstract_memory_size(width); command = set_field(command, AC_ACCESS_MEMORY_AAMPOSTINCREMENT, postincrement); - command = set_field(command, AC_ACCESS_MEMORY_WRITE, write); + command = set_field(command, AC_ACCESS_MEMORY_WRITE, is_write); return command; } @@ -1013,12 +1046,11 @@ static int examine_progbuf(struct target *target) if (info->progbufsize < 1) { info->progbuf_writable = YNM_NO; - LOG_INFO("No program buffer present."); + LOG_TARGET_INFO(target, "No program buffer present."); return ERROR_OK; } - uint64_t s0; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; struct riscv_program program; @@ -1034,9 +1066,6 @@ static int examine_progbuf(struct target *target) riscv_program_insert(&program, sw(S0, S0, 0)); int result = riscv_program_exec(&program, target); - if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) - return ERROR_FAIL; - if (result != ERROR_OK) { /* This program might have failed if the program buffer is not * writable. */ @@ -1045,15 +1074,15 @@ static int examine_progbuf(struct target *target) } uint32_t written; - if (dmi_read(target, &written, DM_PROGBUF0) != ERROR_OK) + if (dm_read(target, &written, DM_PROGBUF0) != ERROR_OK) return ERROR_FAIL; if (written == (uint32_t) info->progbuf_address) { - LOG_INFO("progbuf is writable at 0x%" PRIx64, + LOG_TARGET_INFO(target, "progbuf is writable at 0x%" PRIx64, info->progbuf_address); info->progbuf_writable = YNM_YES; } else { - LOG_INFO("progbuf is not writeable at 0x%" PRIx64, + LOG_TARGET_INFO(target, "progbuf is not writeable at 0x%" PRIx64, info->progbuf_address); info->progbuf_writable = YNM_NO; } @@ -1061,7 +1090,7 @@ static int examine_progbuf(struct target *target) return ERROR_OK; } -static int is_fpu_reg(uint32_t gdb_regno) +static int is_fpu_reg(enum gdb_regno gdb_regno) { return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) || (gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) || @@ -1069,46 +1098,66 @@ static int is_fpu_reg(uint32_t gdb_regno) (gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR); } -static int is_vector_reg(uint32_t gdb_regno) +static int is_vector_reg(enum gdb_regno gdb_regno) { return (gdb_regno >= GDB_REGNO_V0 && gdb_regno <= GDB_REGNO_V31) || gdb_regno == GDB_REGNO_VSTART || gdb_regno == GDB_REGNO_VXSAT || gdb_regno == GDB_REGNO_VXRM || + gdb_regno == GDB_REGNO_VCSR || gdb_regno == GDB_REGNO_VL || gdb_regno == GDB_REGNO_VTYPE || gdb_regno == GDB_REGNO_VLENB; } -static int prep_for_register_access(struct target *target, uint64_t *mstatus, - int regno) +static int prep_for_register_access(struct target *target, + riscv_reg_t *orig_mstatus, enum gdb_regno regno) { - if (is_fpu_reg(regno) || is_vector_reg(regno)) { - if (register_read(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) - return ERROR_FAIL; - if (is_fpu_reg(regno) && (*mstatus & MSTATUS_FS) == 0) { - if (register_write_direct(target, GDB_REGNO_MSTATUS, - set_field(*mstatus, MSTATUS_FS, 1)) != ERROR_OK) - return ERROR_FAIL; - } else if (is_vector_reg(regno) && (*mstatus & MSTATUS_VS) == 0) { - if (register_write_direct(target, GDB_REGNO_MSTATUS, - set_field(*mstatus, MSTATUS_VS, 1)) != ERROR_OK) - return ERROR_FAIL; - } - } else { - *mstatus = 0; + assert(orig_mstatus); + + if (!is_fpu_reg(regno) && !is_vector_reg(regno)) { + /* If we don't assign orig_mstatus, clang static analysis + * complains when this value is passed to + * cleanup_after_register_access(). */ + *orig_mstatus = 0; + /* No special preparation needed */ + return ERROR_OK; } + + LOG_TARGET_DEBUG(target, "Preparing mstatus to access %s", + riscv_reg_gdb_regno_name(target, regno)); + + assert(target->state == TARGET_HALTED && + "The target must be halted to modify and then restore mstatus"); + + if (riscv_reg_get(target, orig_mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) + return ERROR_FAIL; + + riscv_reg_t new_mstatus = *orig_mstatus; + riscv_reg_t field_mask = is_fpu_reg(regno) ? MSTATUS_FS : MSTATUS_VS; + + if ((new_mstatus & field_mask) != 0) + return ERROR_OK; + + new_mstatus = set_field(new_mstatus, field_mask, 1); + + if (riscv_reg_write(target, GDB_REGNO_MSTATUS, new_mstatus) != ERROR_OK) + return ERROR_FAIL; + + LOG_TARGET_DEBUG(target, "Prepared to access %s (mstatus=0x%" PRIx64 ")", + riscv_reg_gdb_regno_name(target, regno), new_mstatus); return ERROR_OK; } static int cleanup_after_register_access(struct target *target, - uint64_t mstatus, int regno) + riscv_reg_t mstatus, enum gdb_regno regno) { - if ((is_fpu_reg(regno) && (mstatus & MSTATUS_FS) == 0) || - (is_vector_reg(regno) && (mstatus & MSTATUS_VS) == 0)) - if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK) - return ERROR_FAIL; - return ERROR_OK; + if (!is_fpu_reg(regno) && !is_vector_reg(regno)) + /* Mstatus was not changed for this register access. No need to restore it. */ + return ERROR_OK; + + LOG_TARGET_DEBUG(target, "Restoring mstatus to 0x%" PRIx64, mstatus); + return riscv_reg_write(target, GDB_REGNO_MSTATUS, mstatus); } typedef enum { @@ -1186,7 +1235,7 @@ static int scratch_reserve(struct target *target, return ERROR_OK; } - LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure " + LOG_TARGET_ERROR(target, "Couldn't find %d bytes of scratch RAM to use. Please configure " "a work area with 'configure -work-area-phys'.", size_bytes); return ERROR_FAIL; } @@ -1203,34 +1252,35 @@ static int scratch_read64(struct target *target, scratch_mem_t *scratch, uint32_t v; switch (scratch->memory_space) { case SPACE_DM_DATA: - if (dmi_read(target, &v, DM_DATA0 + scratch->debug_address) != ERROR_OK) + if (dm_read(target, &v, DM_DATA0 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value = v; - if (dmi_read(target, &v, DM_DATA1 + scratch->debug_address) != ERROR_OK) + if (dm_read(target, &v, DM_DATA1 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value |= ((uint64_t) v) << 32; break; case SPACE_DMI_PROGBUF: - if (dmi_read(target, &v, DM_PROGBUF0 + scratch->debug_address) != ERROR_OK) + if (dm_read(target, &v, DM_PROGBUF0 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value = v; - if (dmi_read(target, &v, DM_PROGBUF1 + scratch->debug_address) != ERROR_OK) + if (dm_read(target, &v, DM_PROGBUF1 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value |= ((uint64_t) v) << 32; break; case SPACE_DMI_RAM: { uint8_t buffer[8] = {0}; - if (read_memory(target, scratch->debug_address, 4, 2, buffer, 4) != ERROR_OK) + const struct riscv_mem_access_args args = { + .address = scratch->debug_address, + .read_buffer = buffer, + .size = 4, + .count = 2, + .increment = 4, + }; + if (riscv013_access_memory(target, args) != ERROR_OK) return ERROR_FAIL; - *value = buffer[0] | - (((uint64_t) buffer[1]) << 8) | - (((uint64_t) buffer[2]) << 16) | - (((uint64_t) buffer[3]) << 24) | - (((uint64_t) buffer[4]) << 32) | - (((uint64_t) buffer[5]) << 40) | - (((uint64_t) buffer[6]) << 48) | - (((uint64_t) buffer[7]) << 56); + *value = buf_get_u64(buffer, + /* first = */ 0, /* bit_num = */ 64); } break; } @@ -1242,12 +1292,13 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch, { switch (scratch->memory_space) { case SPACE_DM_DATA: - dmi_write(target, DM_DATA0 + scratch->debug_address, value); - dmi_write(target, DM_DATA1 + scratch->debug_address, value >> 32); + dm_write(target, DM_DATA0 + scratch->debug_address, (uint32_t)value); + dm_write(target, DM_DATA1 + scratch->debug_address, (uint32_t)(value >> 32)); break; case SPACE_DMI_PROGBUF: - dmi_write(target, DM_PROGBUF0 + scratch->debug_address, value); - dmi_write(target, DM_PROGBUF1 + scratch->debug_address, value >> 32); + dm_write(target, DM_PROGBUF0 + scratch->debug_address, (uint32_t)value); + dm_write(target, DM_PROGBUF1 + scratch->debug_address, (uint32_t)(value >> 32)); + riscv013_invalidate_cached_progbuf(target); break; case SPACE_DMI_RAM: { @@ -1261,7 +1312,14 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch, value >> 48, value >> 56 }; - if (write_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK) + const struct riscv_mem_access_args args = { + .address = scratch->debug_address, + .write_buffer = buffer, + .size = 4, + .count = 2, + .increment = 4, + }; + if (riscv013_access_memory(target, args) != ERROR_OK) return ERROR_FAIL; } break; @@ -1270,7 +1328,7 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch, } /** Return register size in bits. */ -static unsigned int register_size(struct target *target, unsigned int number) +static unsigned int register_size(struct target *target, enum gdb_regno number) { /* If reg_cache hasn't been initialized yet, make a guess. We need this for * when this function is called during examine(). */ @@ -1283,210 +1341,312 @@ static unsigned int register_size(struct target *target, unsigned int number) static bool has_sufficient_progbuf(struct target *target, unsigned int size) { RISCV013_INFO(info); - RISCV_INFO(r); + return info->progbufsize + info->impebreak >= size; +} - return info->progbufsize + r->impebreak >= size; +/** + * This function is used to read a 64-bit value from a register by executing a + * program. + * The program stores a register to address located in S0. + * The caller should save S0. + */ +static int internal_register_read64_progbuf_scratch(struct target *target, + struct riscv_program *program, riscv_reg_t *value) +{ + scratch_mem_t scratch; + + if (scratch_reserve(target, &scratch, program, 8) != ERROR_OK) + return ERROR_FAIL; + + if (register_write_abstract(target, GDB_REGNO_S0, scratch.hart_address) + != ERROR_OK) { + scratch_release(target, &scratch); + return ERROR_FAIL; + } + if (riscv_program_exec(program, target) != ERROR_OK) { + scratch_release(target, &scratch); + return ERROR_FAIL; + } + + int result = scratch_read64(target, &scratch, value); + + scratch_release(target, &scratch); + return result; +} + +static int fpr_read_progbuf(struct target *target, uint64_t *value, + enum gdb_regno number) +{ + assert(target->state == TARGET_HALTED); + assert(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31); + + const unsigned int freg = number - GDB_REGNO_FPR0; + + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) { + /* There are no instructions to move all the bits from a + * register, so we need to use some scratch RAM. + */ + if (riscv_program_insert(&program, fsd(freg, S0, 0)) != ERROR_OK) + return ERROR_FAIL; + return internal_register_read64_progbuf_scratch(target, &program, value); + } + if (riscv_program_insert(&program, + riscv_supports_extension(target, 'D') ? + fmv_x_d(S0, freg) : fmv_x_w(S0, freg)) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_program_exec(&program, target) != ERROR_OK) + return ERROR_FAIL; + + return register_read_abstract(target, value, GDB_REGNO_S0) != ERROR_OK; +} + +static int csr_read_progbuf(struct target *target, uint64_t *value, + enum gdb_regno number) +{ + assert(target->state == TARGET_HALTED); + assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095); + + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + if (riscv_program_csrr(&program, S0, number) != ERROR_OK) + return ERROR_FAIL; + if (riscv_program_exec(&program, target) != ERROR_OK) + return ERROR_FAIL; + + return register_read_abstract(target, value, GDB_REGNO_S0) != ERROR_OK; +} + +/** + * This function reads a register by writing a program to program buffer and + * executing it. + */ +static int register_read_progbuf(struct target *target, uint64_t *value, + enum gdb_regno number) +{ + assert(target->state == TARGET_HALTED); + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) + return fpr_read_progbuf(target, value, number); + else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) + return csr_read_progbuf(target, value, number); + + LOG_TARGET_ERROR(target, "Unexpected read of %s via program buffer.", + riscv_reg_gdb_regno_name(target, number)); + return ERROR_FAIL; +} + +/** + * This function is used to write a 64-bit value to a register by executing a + * program. + * The program loads a value from address located in S0 to a register. + * The caller should save S0. + */ +static int internal_register_write64_progbuf_scratch(struct target *target, + struct riscv_program *program, riscv_reg_t value) +{ + scratch_mem_t scratch; + + if (scratch_reserve(target, &scratch, program, 8) != ERROR_OK) + return ERROR_FAIL; + + if (register_write_abstract(target, GDB_REGNO_S0, scratch.hart_address) + != ERROR_OK) { + scratch_release(target, &scratch); + return ERROR_FAIL; + } + if (scratch_write64(target, &scratch, value) != ERROR_OK) { + scratch_release(target, &scratch); + return ERROR_FAIL; + } + int result = riscv_program_exec(program, target); + + scratch_release(target, &scratch); + return result; +} + +static int fpr_write_progbuf(struct target *target, enum gdb_regno number, + riscv_reg_t value) +{ + assert(target->state == TARGET_HALTED); + assert(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31); + const unsigned int freg = number - GDB_REGNO_FPR0; + + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + + if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) { + /* There are no instructions to move all the bits from a register, + * so we need to use some scratch RAM. + */ + if (riscv_program_insert(&program, fld(freg, S0, 0)) != ERROR_OK) + return ERROR_FAIL; + return internal_register_write64_progbuf_scratch(target, &program, value); + } + + if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_program_insert(&program, + riscv_supports_extension(target, 'D') ? + fmv_d_x(freg, S0) : fmv_w_x(freg, S0)) != ERROR_OK) + return ERROR_FAIL; + + return riscv_program_exec(&program, target); +} + +static int vtype_write_progbuf(struct target *target, riscv_reg_t value) +{ + assert(target->state == TARGET_HALTED); + + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; + if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + if (riscv_program_insert(&program, csrr(S1, CSR_VL)) != ERROR_OK) + return ERROR_FAIL; + if (riscv_program_insert(&program, vsetvl(ZERO, S1, S0)) != ERROR_OK) + return ERROR_FAIL; + + return riscv_program_exec(&program, target); +} + +static int vl_write_progbuf(struct target *target, riscv_reg_t value) +{ + assert(target->state == TARGET_HALTED); + + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; + if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + if (riscv_program_insert(&program, csrr(S1, CSR_VTYPE)) != ERROR_OK) + return ERROR_FAIL; + if (riscv_program_insert(&program, vsetvl(ZERO, S0, S1)) != ERROR_OK) + return ERROR_FAIL; + + return riscv_program_exec(&program, target); +} + +static int csr_write_progbuf(struct target *target, enum gdb_regno number, + riscv_reg_t value) +{ + assert(target->state == TARGET_HALTED); + assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095); + + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + if (riscv_program_csrw(&program, S0, number) != ERROR_OK) + return ERROR_FAIL; + + return riscv_program_exec(&program, target); +} + +/** + * This function writes a register by writing a program to program buffer and + * executing it. + */ +static int register_write_progbuf(struct target *target, enum gdb_regno number, + riscv_reg_t value) +{ + assert(target->state == TARGET_HALTED); + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) + return fpr_write_progbuf(target, number, value); + else if (number == GDB_REGNO_VTYPE) + return vtype_write_progbuf(target, value); + else if (number == GDB_REGNO_VL) + return vl_write_progbuf(target, value); + else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) + return csr_write_progbuf(target, number, value); + + LOG_TARGET_ERROR(target, "Unexpected write to %s via program buffer.", + riscv_reg_gdb_regno_name(target, number)); + return ERROR_FAIL; } /** * Immediately write the new value to the requested register. This mechanism * bypasses any caches. */ -static int register_write_direct(struct target *target, unsigned int number, - uint64_t value) +static int register_write_direct(struct target *target, enum gdb_regno number, + riscv_reg_t value) { - LOG_DEBUG("{%d} %s <- 0x%" PRIx64, riscv_current_hartid(target), - gdb_regno_name(number), value); + LOG_TARGET_DEBUG(target, "Writing 0x%" PRIx64 " to %s", value, + riscv_reg_gdb_regno_name(target, number)); - int result = register_write_abstract(target, number, value, - register_size(target, number)); - if (result == ERROR_OK || !has_sufficient_progbuf(target, 2) || - !riscv_is_halted(target)) - return result; + if (target->state != TARGET_HALTED) + return register_write_abstract(target, number, value); - struct riscv_program program; - riscv_program_init(&program, target); - - uint64_t s0; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) - return ERROR_FAIL; - - uint64_t mstatus; + riscv_reg_t mstatus; if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) return ERROR_FAIL; - scratch_mem_t scratch; - bool use_scratch = false; - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - riscv_supports_extension(target, 'D') && - riscv_xlen(target) < 64) { - /* There are no instructions to move all the bits from a register, so - * we need to use some scratch RAM. */ - use_scratch = true; - riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0)); + int result = register_write_abstract(target, number, value); - if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK) - return ERROR_FAIL; - - if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address) - != ERROR_OK) { - scratch_release(target, &scratch); - return ERROR_FAIL; - } - - if (scratch_write64(target, &scratch, value) != ERROR_OK) { - scratch_release(target, &scratch); - return ERROR_FAIL; - } - - } else if (number == GDB_REGNO_VTYPE) { - riscv_program_insert(&program, csrr(S0, CSR_VL)); - riscv_program_insert(&program, vsetvli(ZERO, S0, value)); - - } else { - if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK) - return ERROR_FAIL; - - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - if (riscv_supports_extension(target, 'D')) - riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0)); - else - riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0)); - } else if (number == GDB_REGNO_VL) { - /* "The XLEN-bit-wide read-only vl CSR can only be updated by the - * vsetvli and vsetvl instructions, and the fault-only-rst vector - * load instruction variants." */ - riscv_reg_t vtype; - if (register_read(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_insert(&program, vsetvli(ZERO, S0, vtype)) != ERROR_OK) - return ERROR_FAIL; - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - riscv_program_csrw(&program, S0, number); - } else { - LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number); - return ERROR_FAIL; - } - } - - int exec_out = riscv_program_exec(&program, target); - /* Don't message on error. Probably the register doesn't exist. */ - if (exec_out == ERROR_OK && target->reg_cache) { - struct reg *reg = &target->reg_cache->reg_list[number]; - buf_set_u64(reg->value, 0, reg->size, value); - } - - if (use_scratch) - scratch_release(target, &scratch); + if (result != ERROR_OK && target->state == TARGET_HALTED) + result = register_write_progbuf(target, number, value); if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK) return ERROR_FAIL; - /* Restore S0. */ - if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) - return ERROR_FAIL; + if (result == ERROR_OK) + LOG_TARGET_DEBUG(target, "%s <- 0x%" PRIx64, riscv_reg_gdb_regno_name(target, number), + value); - return exec_out; -} - -/** Read register value from the target. Also update the cached value. */ -static int register_read(struct target *target, uint64_t *value, uint32_t number) -{ - if (number == GDB_REGNO_ZERO) { - *value = 0; - return ERROR_OK; - } - int result = register_read_direct(target, value, number); - if (result != ERROR_OK) - return ERROR_FAIL; - if (target->reg_cache) { - struct reg *reg = &target->reg_cache->reg_list[number]; - buf_set_u64(reg->value, 0, reg->size, *value); - } - return ERROR_OK; + return result; } /** Actually read registers from the target right now. */ -static int register_read_direct(struct target *target, uint64_t *value, uint32_t number) +static int register_read_direct(struct target *target, riscv_reg_t *value, + enum gdb_regno number) { - int result = register_read_abstract(target, value, number, - register_size(target, number)); + LOG_TARGET_DEBUG(target, "Reading %s", riscv_reg_gdb_regno_name(target, number)); - if (result != ERROR_OK && - has_sufficient_progbuf(target, 2) && - number > GDB_REGNO_XPR31) { - struct riscv_program program; - riscv_program_init(&program, target); + if (target->state != TARGET_HALTED) + return register_read_abstract(target, value, number); - scratch_mem_t scratch; - bool use_scratch = false; + riscv_reg_t mstatus; - riscv_reg_t s0; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) - return ERROR_FAIL; + if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) + return ERROR_FAIL; - /* Write program to move data into s0. */ + int result = register_read_abstract(target, value, number); - uint64_t mstatus; - if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) - return ERROR_FAIL; + if (result != ERROR_OK && target->state == TARGET_HALTED) + result = register_read_progbuf(target, value, number); - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - if (riscv_supports_extension(target, 'D') - && riscv_xlen(target) < 64) { - /* There are no instructions to move all the bits from a - * register, so we need to use some scratch RAM. */ - riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0, - 0)); + if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK) + return ERROR_FAIL; - if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK) - return ERROR_FAIL; - use_scratch = true; - - if (register_write_direct(target, GDB_REGNO_S0, - scratch.hart_address) != ERROR_OK) { - scratch_release(target, &scratch); - return ERROR_FAIL; - } - } else if (riscv_supports_extension(target, 'D')) { - riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0)); - } else { - riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0)); - } - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - riscv_program_csrr(&program, S0, number); - } else { - LOG_ERROR("Unsupported register: %s", gdb_regno_name(number)); - return ERROR_FAIL; - } - - /* Execute program. */ - result = riscv_program_exec(&program, target); - /* Don't message on error. Probably the register doesn't exist. */ - - if (use_scratch) { - result = scratch_read64(target, &scratch, value); - scratch_release(target, &scratch); - if (result != ERROR_OK) - return result; - } else { - /* Read S0 */ - if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK) - return ERROR_FAIL; - } - - if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK) - return ERROR_FAIL; - - /* Restore S0. */ - if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) - return ERROR_FAIL; - } - - if (result == ERROR_OK) { - LOG_DEBUG("{%d} %s = 0x%" PRIx64, riscv_current_hartid(target), - gdb_regno_name(number), *value); - } + if (result == ERROR_OK) + LOG_TARGET_DEBUG(target, "%s = 0x%" PRIx64, riscv_reg_gdb_regno_name(target, number), + *value); return result; } @@ -1502,10 +1662,10 @@ static int wait_for_authbusy(struct target *target, uint32_t *dmstatus) *dmstatus = value; if (!get_field(value, DM_DMSTATUS_AUTHBUSY)) break; - if (time(NULL) - start > riscv_command_timeout_sec) { - LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dmstatus=0x%x). " + if (time(NULL) - start > riscv_get_command_timeout_sec()) { + LOG_TARGET_ERROR(target, "Timed out after %ds waiting for authbusy to go low (dmstatus=0x%x). " "Increase the timeout with riscv set_command_timeout_sec.", - riscv_command_timeout_sec, + riscv_get_command_timeout_sec(), value); return ERROR_FAIL; } @@ -1514,125 +1674,404 @@ static int wait_for_authbusy(struct target *target, uint32_t *dmstatus) return ERROR_OK; } +static int set_dcsr_ebreak(struct target *target, bool step) +{ + LOG_TARGET_DEBUG(target, "Set dcsr.ebreak*"); + + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + RISCV013_INFO(info); + riscv_reg_t original_dcsr, dcsr; + /* We want to twiddle some bits in the debug CSR so debugging works. */ + if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + original_dcsr = dcsr; + dcsr = set_field(dcsr, CSR_DCSR_STEP, step); + const struct riscv_private_config * const config = riscv_private_config(target); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, config->dcsr_ebreak_fields[RISCV_MODE_M]); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, config->dcsr_ebreak_fields[RISCV_MODE_S]); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, config->dcsr_ebreak_fields[RISCV_MODE_U]); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, config->dcsr_ebreak_fields[RISCV_MODE_VS]); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, config->dcsr_ebreak_fields[RISCV_MODE_VU]); + if (dcsr != original_dcsr && + riscv_reg_set(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK) + return ERROR_FAIL; + info->dcsr_ebreak_is_set = true; + return ERROR_OK; +} + +static int halt_set_dcsr_ebreak(struct target *target) +{ + RISCV_INFO(r); + RISCV013_INFO(info); + LOG_TARGET_DEBUG(target, "Halt to set DCSR.ebreak*"); + + /* Remove this hart from the halt group. This won't work on all targets + * because the debug spec allows halt groups to be hard-coded, but I + * haven't actually encountered those in the wild yet. + * + * There is a possible race condition when another hart halts, and + * this one is expected to also halt because it's supposed to be in the + * same halt group. Or when this hart is halted when that happens. + * + * A better solution might be to leave the halt groups alone, and track + * why we're halting when a halt occurs. When there are halt groups, + * that leads to extra halting if not all harts need to set dcsr.ebreak + * at the same time. It also makes for more complicated code. + * + * The perfect solution would be Quick Access, but I'm not aware of any + * hardware that implements it. + * + * We don't need a perfect solution, because we only get here when a + * hart spontaneously resets, or when it powers down and back up again. + * Those are both relatively rare. (At least I hope so. Maybe some + * design just powers each hart down for 90ms out of every 100ms) + */ + + + if (info->haltgroup_supported) { + bool supported; + if (set_group(target, &supported, 0, HALT_GROUP) != ERROR_OK) + return ERROR_FAIL; + if (!supported) + LOG_TARGET_ERROR(target, "Couldn't place hart in halt group 0. " + "Some harts may be unexpectedly halted."); + } + + int result = ERROR_OK; + + r->prepped = true; + if (riscv013_halt_go(target) != ERROR_OK || + set_dcsr_ebreak(target, false) != ERROR_OK || + riscv013_step_or_resume_current_hart(target, false) != ERROR_OK) { + result = ERROR_FAIL; + } else { + target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; + } + + /* Add it back to the halt group. */ + if (info->haltgroup_supported) { + bool supported; + if (set_group(target, &supported, target->smp, HALT_GROUP) != ERROR_OK) + return ERROR_FAIL; + if (!supported) + LOG_TARGET_ERROR(target, "Couldn't place hart back in halt group %d. " + "Some harts may be unexpectedly halted.", target->smp); + } + + return result; +} + /*** OpenOCD target functions. ***/ static void deinit_target(struct target *target) { - LOG_DEBUG("riscv_deinit_target()"); + LOG_TARGET_DEBUG(target, "Deinitializing target."); struct riscv_info *info = target->arch_info; if (!info) return; + riscv013_info_t *vsinfo = info->version_specific; + if (vsinfo) + ac_cache_free(&vsinfo->ac_not_supported_cache); + + riscv013_dm_free(target); + free(info->version_specific); /* TODO: free register arch_info */ info->version_specific = NULL; } -static int set_haltgroup(struct target *target, bool *supported) +static int set_group(struct target *target, bool *supported, unsigned int group, + enum grouptype grouptype) { - uint32_t write = set_field(DM_DMCS2_HGWRITE, DM_DMCS2_GROUP, target->smp); - if (dmi_write(target, DM_DMCS2, write) != ERROR_OK) + uint32_t write_val = DM_DMCS2_HGWRITE; + assert(group <= 31); + write_val = set_field(write_val, DM_DMCS2_GROUP, group); + write_val = set_field(write_val, DM_DMCS2_GROUPTYPE, (grouptype == HALT_GROUP) ? 0 : 1); + if (dm_write(target, DM_DMCS2, write_val) != ERROR_OK) return ERROR_FAIL; - uint32_t read; - if (dmi_read(target, &read, DM_DMCS2) != ERROR_OK) + uint32_t read_val; + if (dm_read(target, &read_val, DM_DMCS2) != ERROR_OK) return ERROR_FAIL; - *supported = get_field(read, DM_DMCS2_GROUP) == target->smp; + if (supported) + *supported = (get_field(read_val, DM_DMCS2_GROUP) == group); return ERROR_OK; } -static int discover_vlenb(struct target *target) +static int wait_for_idle_if_needed(struct target *target) { - RISCV_INFO(r); - riscv_reg_t vlenb; - - if (register_read(target, &vlenb, GDB_REGNO_VLENB) != ERROR_OK) { - LOG_WARNING("Couldn't read vlenb for %s; vector register access won't work.", - target_name(target)); - r->vlenb = 0; + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + if (!dm->abstract_cmd_maybe_busy) + /* The previous abstract command ended correctly + * and busy was cleared. No need to do anything. */ return ERROR_OK; + + /* The previous abstract command timed out and abstractcs.busy + * may have remained set. Wait for it to get cleared. */ + uint32_t abstractcs; + int result = wait_for_idle(target, &abstractcs); + if (result != ERROR_OK) + return result; + LOG_DEBUG_REG(target, DM_ABSTRACTCS, abstractcs); + return ERROR_OK; +} + +static int reset_dm(struct target *target) +{ + /* TODO: This function returns an error when a DMI operation fails. + * However, [3.14.2. Debug Module Control] states: + * > 0 (inactive): ... Any accesses to the module may fail. + * + * Ignoring failures may introduce incompatibility with 0.13. + * See https://github.com/riscv/riscv-debug-spec/issues/1021 + */ + dm013_info_t *dm = get_dm(target); + assert(dm && "DM is expected to be already allocated."); + assert(!dm->was_reset && "Attempt to reset an already-reset debug module."); + /* `dmcontrol.hartsel` should be read first, in order not to + * change it when requesting the reset, since changing it + * without checking that `abstractcs.busy` is low is + * prohibited. + */ + uint32_t dmcontrol; + int result = dm_read(target, &dmcontrol, DM_DMCONTROL); + if (result != ERROR_OK) + return result; + + if (get_field32(dmcontrol, DM_DMCONTROL_DMACTIVE)) { + /* `dmcontrol.hartsel` is not changed. */ + dmcontrol = (dmcontrol & DM_DMCONTROL_HARTSELLO) | + (dmcontrol & DM_DMCONTROL_HARTSELHI); + LOG_TARGET_DEBUG(target, "Initiating DM reset."); + result = dm_write(target, DM_DMCONTROL, dmcontrol); + if (result != ERROR_OK) + return result; + + const time_t start = time(NULL); + LOG_TARGET_DEBUG(target, "Waiting for the DM to acknowledge reset."); + do { + result = dm_read(target, &dmcontrol, DM_DMCONTROL); + if (result != ERROR_OK) + return result; + + if (time(NULL) - start > riscv_get_command_timeout_sec()) { + LOG_TARGET_ERROR(target, "DM didn't acknowledge reset in %d s. " + "Increase the timeout with 'riscv set_command_timeout_sec'.", + riscv_get_command_timeout_sec()); + return ERROR_TIMEOUT_REACHED; + } + } while (get_field32(dmcontrol, DM_DMCONTROL_DMACTIVE)); + LOG_TARGET_DEBUG(target, "DM reset initiated."); } - r->vlenb = vlenb; - LOG_INFO("Vector support with vlenb=%d", r->vlenb); + LOG_TARGET_DEBUG(target, "Activating the DM."); + result = dm_write(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE); + if (result != ERROR_OK) + return result; + const time_t start = time(NULL); + LOG_TARGET_DEBUG(target, "Waiting for the DM to come out of reset."); + do { + result = dm_read(target, &dmcontrol, DM_DMCONTROL); + if (result != ERROR_OK) + return result; + + if (time(NULL) - start > riscv_get_command_timeout_sec()) { + LOG_TARGET_ERROR(target, "Debug Module did not become active in %d s. " + "Increase the timeout with 'riscv set_command_timeout_sec'.", + riscv_get_command_timeout_sec()); + return ERROR_TIMEOUT_REACHED; + } + } while (!get_field32(dmcontrol, DM_DMCONTROL_DMACTIVE)); + + LOG_TARGET_DEBUG(target, "DM successfully reset."); + dm->was_reset = true; + return ERROR_OK; +} + +static int examine_dm(struct target *target) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + if (dm->was_examined) + return ERROR_OK; + + int result = ERROR_FAIL; + + if (dm->was_reset) { + /* The DM was already reset when examining a different hart. + * No need to reset it again. But for safety, assume that an abstract + * command might be in progress at the moment. + */ + dm->abstract_cmd_maybe_busy = true; + } else { + result = reset_dm(target); + if (result != ERROR_OK) + return result; + } + + dm->current_hartid = HART_INDEX_UNKNOWN; + + result = dm_write(target, DM_DMCONTROL, DM_DMCONTROL_HARTSELLO | + DM_DMCONTROL_HARTSELHI | DM_DMCONTROL_DMACTIVE | + DM_DMCONTROL_HASEL); + if (result != ERROR_OK) + return result; + + uint32_t dmcontrol; + result = dm_read(target, &dmcontrol, DM_DMCONTROL); + if (result != ERROR_OK) + return result; + + dm->hasel_supported = get_field(dmcontrol, DM_DMCONTROL_HASEL); + + uint32_t hartsel = + (get_field(dmcontrol, DM_DMCONTROL_HARTSELHI) << + DM_DMCONTROL_HARTSELLO_LENGTH) | + get_field(dmcontrol, DM_DMCONTROL_HARTSELLO); + + /* Before doing anything else we must first enumerate the harts. */ + const int max_hart_count = MIN(RISCV_MAX_HARTS, hartsel + 1); + if (dm->hart_count < 0) { + for (int i = 0; i < max_hart_count; ++i) { + /* TODO: This is extremely similar to + * riscv013_get_hart_state(). + * It would be best to reuse the code. + */ + result = dm013_select_hart(target, i); + if (result != ERROR_OK) + return result; + + uint32_t s; + result = dmstatus_read(target, &s, /*authenticated*/ true); + if (result != ERROR_OK) + return result; + + if (get_field(s, DM_DMSTATUS_ANYNONEXISTENT)) + break; + + dm->hart_count = i + 1; + + if (get_field(s, DM_DMSTATUS_ANYHAVERESET)) { + dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET; + /* If `abstractcs.busy` is set, debugger should not + * change `hartsel`. + */ + result = wait_for_idle_if_needed(target); + if (result != ERROR_OK) + return result; + dmcontrol = set_dmcontrol_hartsel(dmcontrol, i); + result = dm_write(target, DM_DMCONTROL, dmcontrol); + if (result != ERROR_OK) + return result; + } + } + LOG_TARGET_DEBUG(target, "Detected %d harts.", dm->hart_count); + } + + if (dm->hart_count <= 0) { + LOG_TARGET_ERROR(target, "No harts found!"); + return ERROR_FAIL; + } + + dm->was_examined = true; return ERROR_OK; } static int examine(struct target *target) { - /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ + /* We reset target state in case if something goes wrong during examine: + * DTM/DM scans could fail or hart may fail to halt. */ + target->state = TARGET_UNKNOWN; + target->debug_reason = DBG_REASON_UNDEFINED; - uint32_t dtmcontrol = dtmcontrol_scan(target, 0); - LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); - LOG_DEBUG(" dmireset=%d", get_field(dtmcontrol, DTM_DTMCS_DMIRESET)); - LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTM_DTMCS_IDLE)); - LOG_DEBUG(" dmistat=%d", get_field(dtmcontrol, DTM_DTMCS_DMISTAT)); - LOG_DEBUG(" abits=%d", get_field(dtmcontrol, DTM_DTMCS_ABITS)); - LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTM_DTMCS_VERSION)); - if (dtmcontrol == 0) { - LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power."); + /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ + LOG_TARGET_DEBUG(target, "dbgbase=0x%x", target->dbgbase); + + uint32_t dtmcontrol; + if (dtmcs_scan(target->tap, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) { + LOG_TARGET_ERROR(target, "Could not scan dtmcontrol. Check JTAG connectivity/board power."); return ERROR_FAIL; } + + LOG_TARGET_DEBUG(target, "dtmcontrol=0x%x", dtmcontrol); + LOG_DEBUG_REG(target, DTM_DTMCS, dtmcontrol); + if (get_field(dtmcontrol, DTM_DTMCS_VERSION) != 1) { - LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)", - get_field(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol); + LOG_TARGET_ERROR(target, "Unsupported DTM version %" PRIu32 ". (dtmcontrol=0x%" PRIx32 ")", + get_field32(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol); return ERROR_FAIL; } riscv013_info_t *info = get_info(target); - /* TODO: This won't be true if there are multiple DMs. */ + info->index = target->coreid; info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS); info->dtmcs_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE); - /* Reset the Debug Module. */ - dm013_info_t *dm = get_dm(target); - if (!dm) - return ERROR_FAIL; - if (!dm->was_reset) { - dmi_write(target, DM_DMCONTROL, 0); - dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE); - dm->was_reset = true; - } - - dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_HARTSELLO | - DM_DMCONTROL_HARTSELHI | DM_DMCONTROL_DMACTIVE | - DM_DMCONTROL_HASEL); - uint32_t dmcontrol; - if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) - return ERROR_FAIL; - - if (!get_field(dmcontrol, DM_DMCONTROL_DMACTIVE)) { - LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x", - dmcontrol); + if (info->abits > RISCV013_DTMCS_ABITS_MAX) { + /* Max. address width given by the debug specification is exceeded */ + LOG_TARGET_ERROR(target, "The target's debug bus (DMI) address width exceeds " + "the maximum:"); + LOG_TARGET_ERROR(target, " found dtmcs.abits = %d; maximum is abits = %d.", + info->abits, RISCV013_DTMCS_ABITS_MAX); return ERROR_FAIL; } - dm->hasel_supported = get_field(dmcontrol, DM_DMCONTROL_HASEL); + if (info->abits == 0) { + LOG_TARGET_ERROR(target, + "dtmcs.abits is zero. Check JTAG connectivity/board power"); + return ERROR_FAIL; + } + if (info->abits < RISCV013_DTMCS_ABITS_MIN) { + /* The requirement for minimum DMI address width of 7 bits is part of + * the RISC-V Debug spec since Jan-20-2017 (commit 03df6ee7). However, + * implementations exist that implement narrower DMI address. For example + * Spike as of Q1/2025 uses dmi.abits = 6. + * + * For that reason, warn the user but continue. + */ + LOG_TARGET_WARNING(target, "The target's debug bus (DMI) address width is " + "lower than the minimum:"); + LOG_TARGET_WARNING(target, " found dtmcs.abits = %d; minimum is abits = %d.", + info->abits, RISCV013_DTMCS_ABITS_MIN); + } + + if (!check_dbgbase_exists(target)) { + LOG_TARGET_ERROR(target, "Could not find debug module with DMI base address (dbgbase) = 0x%x", target->dbgbase); + return ERROR_FAIL; + } + + int result = examine_dm(target); + if (result != ERROR_OK) + return result; + + result = dm013_select_target(target); + if (result != ERROR_OK) + return result; + + /* We're here because we're uncertain about the state of the target. That + * includes our progbuf cache. */ + riscv013_invalidate_cached_progbuf(target); uint32_t dmstatus; if (dmstatus_read(target, &dmstatus, false) != ERROR_OK) return ERROR_FAIL; - LOG_DEBUG("dmstatus: 0x%08x", dmstatus); + LOG_TARGET_DEBUG(target, "dmstatus: 0x%08x", dmstatus); int dmstatus_version = get_field(dmstatus, DM_DMSTATUS_VERSION); if (dmstatus_version != 2 && dmstatus_version != 3) { /* Error was already printed out in dmstatus_read(). */ return ERROR_FAIL; } - uint32_t hartsel = - (get_field(dmcontrol, DM_DMCONTROL_HARTSELHI) << - DM_DMCONTROL_HARTSELLO_LENGTH) | - get_field(dmcontrol, DM_DMCONTROL_HARTSELLO); - info->hartsellen = 0; - while (hartsel & 1) { - info->hartsellen++; - hartsel >>= 1; - } - LOG_DEBUG("hartsellen=%d", info->hartsellen); - uint32_t hartinfo; - if (dmi_read(target, &hartinfo, DM_HARTINFO) != ERROR_OK) + if (dm_read(target, &hartinfo, DM_HARTINFO) != ERROR_OK) return ERROR_FAIL; info->datasize = get_field(hartinfo, DM_HARTINFO_DATASIZE); @@ -1640,161 +2079,107 @@ static int examine(struct target *target) info->dataaddr = get_field(hartinfo, DM_HARTINFO_DATAADDR); if (!get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED)) { - LOG_ERROR("Debugger is not authenticated to target Debug Module. " + LOG_TARGET_ERROR(target, "Debugger is not authenticated to target Debug Module. " "(dmstatus=0x%x). Use `riscv authdata_read` and " "`riscv authdata_write` commands to authenticate.", dmstatus); - /* If we return ERROR_FAIL here, then in a multicore setup the next - * core won't be examined, which means we won't set up the - * authentication commands for them, which means the config script - * needs to be a lot more complex. */ - return ERROR_OK; + return ERROR_FAIL; } - if (dmi_read(target, &info->sbcs, DM_SBCS) != ERROR_OK) + if (dm_read(target, &info->sbcs, DM_SBCS) != ERROR_OK) return ERROR_FAIL; /* Check that abstract data registers are accessible. */ uint32_t abstractcs; - if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) + if (dm_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) return ERROR_FAIL; info->datacount = get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); info->progbufsize = get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); - LOG_INFO("datacount=%d progbufsize=%d", info->datacount, info->progbufsize); + LOG_TARGET_INFO(target, "datacount=%d progbufsize=%d", + info->datacount, info->progbufsize); - RISCV_INFO(r); - r->impebreak = get_field(dmstatus, DM_DMSTATUS_IMPEBREAK); + info->impebreak = get_field(dmstatus, DM_DMSTATUS_IMPEBREAK); if (!has_sufficient_progbuf(target, 2)) { - LOG_WARNING("We won't be able to execute fence instructions on this " + LOG_TARGET_WARNING(target, "We won't be able to execute fence instructions on this " "target. Memory may not always appear consistent. " "(progbufsize=%d, impebreak=%d)", info->progbufsize, - r->impebreak); - } - - if (info->progbufsize < 4 && riscv_enable_virtual) { - LOG_ERROR("set_enable_virtual is not available on this target. It " - "requires a program buffer size of at least 4. (progbufsize=%d) " - "Use `riscv set_enable_virtual off` to continue." - , info->progbufsize); - } - - /* Before doing anything else we must first enumerate the harts. */ - if (dm->hart_count < 0) { - for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) { - r->current_hartid = i; - if (riscv013_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - - uint32_t s; - if (dmstatus_read(target, &s, true) != ERROR_OK) - return ERROR_FAIL; - if (get_field(s, DM_DMSTATUS_ANYNONEXISTENT)) - break; - dm->hart_count = i + 1; - - if (get_field(s, DM_DMSTATUS_ANYHAVERESET)) - dmi_write(target, DM_DMCONTROL, - set_hartsel(DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET, i)); - } - - LOG_DEBUG("Detected %d harts.", dm->hart_count); - } - - r->current_hartid = target->coreid; - - if (dm->hart_count == 0) { - LOG_ERROR("No harts found!"); - return ERROR_FAIL; + info->impebreak); } /* Don't call any riscv_* functions until after we've counted the number of * cores and initialized registers. */ - if (riscv013_select_current_hart(target) != ERROR_OK) + enum riscv_hart_state state_at_examine_start; + if (riscv_get_hart_state(target, &state_at_examine_start) != ERROR_OK) return ERROR_FAIL; - bool halted = riscv_is_halted(target); - if (!halted) { + RISCV_INFO(r); + const bool hart_halted_at_examine_start = state_at_examine_start == RISCV_STATE_HALTED; + if (!hart_halted_at_examine_start) { + r->prepped = true; if (riscv013_halt_go(target) != ERROR_OK) { - LOG_ERROR("Fatal: Hart %d failed to halt during examine()", r->current_hartid); + LOG_TARGET_ERROR(target, "Fatal: Hart %d failed to halt during %s", + info->index, __func__); return ERROR_FAIL; } } - /* Without knowing anything else we can at least mess with the - * program buffer. */ - r->debug_buffer_size = info->progbufsize; + target->state = TARGET_HALTED; + target->debug_reason = hart_halted_at_examine_start ? DBG_REASON_UNDEFINED : DBG_REASON_DBGRQ; - int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64); - if (result == ERROR_OK) - r->xlen = 64; - else - r->xlen = 32; + result = riscv013_reg_examine_all(target); + if (result != ERROR_OK) + return result; - if (register_read(target, &r->misa, GDB_REGNO_MISA)) { - LOG_ERROR("Fatal: Failed to read MISA from hart %d.", r->current_hartid); - return ERROR_FAIL; - } - - if (riscv_supports_extension(target, 'V')) { - if (discover_vlenb(target) != ERROR_OK) - return ERROR_FAIL; - } - - /* Now init registers based on what we discovered. */ - if (riscv_init_registers(target) != ERROR_OK) + if (set_dcsr_ebreak(target, false) != ERROR_OK) return ERROR_FAIL; - /* Display this as early as possible to help people who are using - * really slow simulators. */ - LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen, - r->misa); - - if (!halted) - riscv013_step_or_resume_current_hart(target, false, false); - - target_set_examined(target); + if (state_at_examine_start == RISCV_STATE_RUNNING) { + riscv013_step_or_resume_current_hart(target, false); + target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; + } else if (state_at_examine_start == RISCV_STATE_HALTED) { + target->state = TARGET_HALTED; + target->debug_reason = DBG_REASON_UNDEFINED; + } if (target->smp) { - bool haltgroup_supported; - if (set_haltgroup(target, &haltgroup_supported) != ERROR_OK) + if (set_group(target, &info->haltgroup_supported, target->smp, HALT_GROUP) != ERROR_OK) return ERROR_FAIL; - if (haltgroup_supported) - LOG_INFO("Core %d made part of halt group %d.", target->coreid, + if (info->haltgroup_supported) + LOG_TARGET_INFO(target, "Core %d made part of halt group %d.", info->index, target->smp); else - LOG_INFO("Core %d could not be made part of halt group %d.", - target->coreid, target->smp); + LOG_TARGET_INFO(target, "Core %d could not be made part of halt group %d.", + info->index, target->smp); } /* Some regression suites rely on seeing 'Examined RISC-V core' to know * when they can connect with gdb/telnet. * We will need to update those suites if we want to change that text. */ - LOG_INFO("Examined RISC-V core; found %d harts", - riscv_count_harts(target)); - LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen, - r->misa); + LOG_TARGET_INFO(target, "Examined RISC-V core"); + LOG_TARGET_INFO(target, " XLEN=%d, misa=0x%" PRIx64, r->xlen, r->misa); return ERROR_OK; } static int riscv013_authdata_read(struct target *target, uint32_t *value, unsigned int index) { if (index > 0) { - LOG_ERROR("Spec 0.13 only has a single authdata register."); + LOG_TARGET_ERROR(target, "Spec 0.13 only has a single authdata register."); return ERROR_FAIL; } if (wait_for_authbusy(target, NULL) != ERROR_OK) return ERROR_FAIL; - return dmi_read(target, value, DM_AUTHDATA); + return dm_read(target, value, DM_AUTHDATA); } static int riscv013_authdata_write(struct target *target, uint32_t value, unsigned int index) { if (index > 0) { - LOG_ERROR("Spec 0.13 only has a single authdata register."); + LOG_TARGET_ERROR(target, "Spec 0.13 only has a single authdata register."); return ERROR_FAIL; } @@ -1802,21 +2187,21 @@ static int riscv013_authdata_write(struct target *target, uint32_t value, unsign if (wait_for_authbusy(target, &before) != ERROR_OK) return ERROR_FAIL; - dmi_write(target, DM_AUTHDATA, value); + dm_write(target, DM_AUTHDATA, value); if (wait_for_authbusy(target, &after) != ERROR_OK) return ERROR_FAIL; if (!get_field(before, DM_DMSTATUS_AUTHENTICATED) && get_field(after, DM_DMSTATUS_AUTHENTICATED)) { - LOG_INFO("authdata_write resulted in successful authentication"); + LOG_TARGET_INFO(target, "authdata_write resulted in successful authentication"); int result = ERROR_OK; dm013_info_t *dm = get_dm(target); if (!dm) return ERROR_FAIL; target_list_t *entry; list_for_each_entry(entry, &dm->target_list, list) { - if (examine(entry->target) != ERROR_OK) + if (target_examine_one(entry->target) != ERROR_OK) result = ERROR_FAIL; } return result; @@ -1825,21 +2210,14 @@ static int riscv013_authdata_write(struct target *target, uint32_t value, unsign return ERROR_OK; } -static int riscv013_hart_count(struct target *target) -{ - dm013_info_t *dm = get_dm(target); - assert(dm); - return dm->hart_count; -} - /* Try to find out the widest memory access size depending on the selected memory access methods. */ static unsigned int riscv013_data_bits(struct target *target) { RISCV013_INFO(info); RISCV_INFO(r); - for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { - int method = r->mem_access_methods[i]; + for (unsigned int i = 0; i < r->num_enabled_mem_access_methods; i++) { + enum riscv_mem_access_method method = r->mem_access_methods[i]; if (method == RISCV_MEM_ACCESS_PROGBUF) { if (has_sufficient_progbuf(target, 3)) @@ -1860,11 +2238,11 @@ static unsigned int riscv013_data_bits(struct target *target) * take those into account as well. For now we assume abstract commands * support XLEN-wide accesses. */ return riscv_xlen(target); - } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED) - /* No further mem access method to try. */ - break; + } else { + assert(false); + } } - LOG_ERROR("Unable to determine supported data bits on this target. Assuming 32 bits."); + LOG_TARGET_ERROR(target, "Unable to determine supported data bits on this target. Assuming 32 bits."); return 32; } @@ -1902,82 +2280,109 @@ static COMMAND_HELPER(riscv013_print_info, struct target *target) return 0; } -static int prep_for_vector_access(struct target *target, uint64_t *vtype, - uint64_t *vl, unsigned int *debug_vl) +static int try_set_vsew(struct target *target, unsigned int *debug_vsew) { RISCV_INFO(r); - /* TODO: this continuous save/restore is terrible for performance. */ - /* Write vtype and vl. */ - unsigned int encoded_vsew; - switch (riscv_xlen(target)) { - case 32: - encoded_vsew = 2; - break; - case 64: - encoded_vsew = 3; - break; - default: - LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target)); + unsigned int encoded_vsew = + (riscv_xlen(target) == 64 && r->vsew64_supported != YNM_NO) ? 3 : 2; + + /* Set standard element width to match XLEN, for vmv instruction to move + * the least significant bits into a GPR. + */ + if (riscv_reg_write(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK) + return ERROR_FAIL; + + if (encoded_vsew == 3 && r->vsew64_supported == YNM_MAYBE) { + /* Check that it's supported. */ + riscv_reg_t vtype; + + if (riscv_reg_get(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK) return ERROR_FAIL; + if (vtype >> (riscv_xlen(target) - 1)) { + r->vsew64_supported = YNM_NO; + /* Try again. */ + return try_set_vsew(target, debug_vsew); + } + r->vsew64_supported = YNM_YES; } + *debug_vsew = encoded_vsew == 3 ? 64 : 32; + return ERROR_OK; +} + +static int prep_for_vector_access(struct target *target, + riscv_reg_t *orig_mstatus, riscv_reg_t *orig_vtype, riscv_reg_t *orig_vl, + unsigned int *debug_vl, unsigned int *debug_vsew) +{ + assert(orig_mstatus); + assert(orig_vtype); + assert(orig_vl); + assert(debug_vl); + assert(debug_vsew); + + RISCV_INFO(r); + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, + "Unable to access vector register: target not halted"); + return ERROR_FAIL; + } + if (prep_for_register_access(target, orig_mstatus, GDB_REGNO_VL) != ERROR_OK) + return ERROR_FAIL; /* Save vtype and vl. */ - if (register_read(target, vtype, GDB_REGNO_VTYPE) != ERROR_OK) + if (riscv_reg_get(target, orig_vtype, GDB_REGNO_VTYPE) != ERROR_OK) return ERROR_FAIL; - if (register_read(target, vl, GDB_REGNO_VL) != ERROR_OK) + if (riscv_reg_get(target, orig_vl, GDB_REGNO_VL) != ERROR_OK) return ERROR_FAIL; - if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK) + if (try_set_vsew(target, debug_vsew) != ERROR_OK) return ERROR_FAIL; - *debug_vl = DIV_ROUND_UP(r->vlenb * 8, riscv_xlen(target)); - if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK) - return ERROR_FAIL; - - return ERROR_OK; + /* Set the number of elements to be updated with results from a vector + * instruction, for the vslide1down instruction. + * Set it so the entire V register is updated. */ + *debug_vl = DIV_ROUND_UP(r->vlenb * 8, *debug_vsew); + return riscv_reg_write(target, GDB_REGNO_VL, *debug_vl); } -static int cleanup_after_vector_access(struct target *target, uint64_t vtype, - uint64_t vl) +static int cleanup_after_vector_access(struct target *target, + riscv_reg_t mstatus, riscv_reg_t vtype, riscv_reg_t vl) { /* Restore vtype and vl. */ - if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK) + if (riscv_reg_write(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK) return ERROR_FAIL; - if (register_write_direct(target, GDB_REGNO_VL, vl) != ERROR_OK) + if (riscv_reg_write(target, GDB_REGNO_VL, vl) != ERROR_OK) return ERROR_FAIL; - return ERROR_OK; + return cleanup_after_register_access(target, mstatus, GDB_REGNO_VL); } -static int riscv013_get_register_buf(struct target *target, - uint8_t *value, int regno) +int riscv013_get_register_buf(struct target *target, uint8_t *value, + enum gdb_regno regno) { assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); - if (riscv_select_current_hart(target) != ERROR_OK) + if (dm013_select_target(target) != ERROR_OK) return ERROR_FAIL; - riscv_reg_t s0; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) + riscv_reg_t mstatus, vtype, vl; + unsigned int debug_vl, debug_vsew; + + if (prep_for_vector_access(target, &mstatus, &vtype, &vl, + &debug_vl, &debug_vsew) != ERROR_OK) return ERROR_FAIL; - uint64_t mstatus; - if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK) - return ERROR_FAIL; - - uint64_t vtype, vl; - unsigned int debug_vl; - if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK) + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; unsigned int vnum = regno - GDB_REGNO_V0; - unsigned int xlen = riscv_xlen(target); - - struct riscv_program program; - riscv_program_init(&program, target); - riscv_program_insert(&program, vmv_x_s(S0, vnum)); - riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true)); int result = ERROR_OK; for (unsigned int i = 0; i < debug_vl; i++) { + /* Can't reuse the same program because riscv_program_exec() adds + * ebreak to the end every time. */ + struct riscv_program program; + riscv_program_init(&program, target); + riscv_program_insert(&program, vmv_x_s(S0, vnum)); + riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true)); + /* Executing the program might result in an exception if there is some * issue with the vector implementation/instructions we're using. If that * happens, attempt to restore as usual. We may have clobbered the @@ -1986,49 +2391,43 @@ static int riscv013_get_register_buf(struct target *target, * so messed up that attempting to restore isn't going to help. */ result = riscv_program_exec(&program, target); if (result == ERROR_OK) { - uint64_t v; + riscv_reg_t v; if (register_read_direct(target, &v, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; - buf_set_u64(value, xlen * i, xlen, v); + buf_set_u64(value, debug_vsew * i, debug_vsew, v); } else { + LOG_TARGET_ERROR(target, + "Failed to execute vmv/vslide1down while reading %s", + riscv_reg_gdb_regno_name(target, regno)); break; } } - if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK) - return ERROR_FAIL; - - if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK) - return ERROR_FAIL; - if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) + if (cleanup_after_vector_access(target, mstatus, vtype, vl) != ERROR_OK) return ERROR_FAIL; return result; } -static int riscv013_set_register_buf(struct target *target, - int regno, const uint8_t *value) +int riscv013_set_register_buf(struct target *target, enum gdb_regno regno, + const uint8_t *value) { assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); - if (riscv_select_current_hart(target) != ERROR_OK) + if (dm013_select_target(target) != ERROR_OK) return ERROR_FAIL; - riscv_reg_t s0; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) + riscv_reg_t mstatus, vtype, vl; + unsigned int debug_vl, debug_vsew; + + if (prep_for_vector_access(target, &mstatus, &vtype, &vl, + &debug_vl, &debug_vsew) != ERROR_OK) return ERROR_FAIL; - uint64_t mstatus; - if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK) - return ERROR_FAIL; - - uint64_t vtype, vl; - unsigned int debug_vl; - if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK) + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; unsigned int vnum = regno - GDB_REGNO_V0; - unsigned int xlen = riscv_xlen(target); struct riscv_program program; riscv_program_init(&program, target); @@ -2036,19 +2435,14 @@ static int riscv013_set_register_buf(struct target *target, int result = ERROR_OK; for (unsigned int i = 0; i < debug_vl; i++) { if (register_write_direct(target, GDB_REGNO_S0, - buf_get_u64(value, xlen * i, xlen)) != ERROR_OK) + buf_get_u64(value, debug_vsew * i, debug_vsew)) != ERROR_OK) return ERROR_FAIL; result = riscv_program_exec(&program, target); if (result != ERROR_OK) break; } - if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK) - return ERROR_FAIL; - - if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK) - return ERROR_FAIL; - if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) + if (cleanup_after_vector_access(target, mstatus, vtype, vl) != ERROR_OK) return ERROR_FAIL; return result; @@ -2072,35 +2466,115 @@ static uint32_t sb_sbaccess(unsigned int size_bytes) return 0; } -static int sb_write_address(struct target *target, target_addr_t address, - bool ensure_success) +static unsigned int get_sbaadress_reg_count(const struct target *target) { RISCV013_INFO(info); - unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); - /* There currently is no support for >64-bit addresses in OpenOCD. */ - if (sbasize > 96) - dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS3, 0, false, false); - if (sbasize > 64) - dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS2, 0, false, false); - if (sbasize > 32) - dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS1, address >> 32, false, false); - return dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS0, address, - false, ensure_success); + const unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); + return DIV_ROUND_UP(sbasize, 32); } -static int batch_run(const struct target *target, struct riscv_batch *batch) +static void batch_fill_sb_write_address(const struct target *target, + struct riscv_batch *batch, target_addr_t address, + enum riscv_scan_delay_class sbaddr0_delay) +{ + /* There currently is no support for >64-bit addresses in OpenOCD. */ + assert(sizeof(target_addr_t) == sizeof(uint64_t)); + const uint32_t addresses[] = {DM_SBADDRESS0, DM_SBADDRESS1, DM_SBADDRESS2, DM_SBADDRESS3}; + const uint32_t values[] = {(uint32_t)address, (uint32_t)(address >> 32), 0, 0}; + const unsigned int reg_count = get_sbaadress_reg_count(target); + assert(reg_count > 0); + assert(reg_count <= ARRAY_SIZE(addresses)); + assert(ARRAY_SIZE(addresses) == ARRAY_SIZE(values)); + + for (unsigned int i = reg_count - 1; i > 0; --i) + riscv_batch_add_dm_write(batch, addresses[i], values[i], /* read back */ true, + RISCV_DELAY_BASE); + riscv_batch_add_dm_write(batch, addresses[0], values[0], /* read back */ true, + sbaddr0_delay); +} + +static int sb_write_address(struct target *target, target_addr_t address, + enum riscv_scan_delay_class sbaddr0_delay) +{ + struct riscv_batch *batch = riscv_batch_alloc(target, + get_sbaadress_reg_count(target)); + batch_fill_sb_write_address(target, batch, address, sbaddr0_delay); + const int res = batch_run_timeout(target, batch); + riscv_batch_free(batch); + return res; +} + +static int batch_run(struct target *target, struct riscv_batch *batch) +{ + RISCV_INFO(r); + RISCV013_INFO(info); + select_dmi(target->tap); + riscv_batch_add_nop(batch); + const int result = riscv_batch_run_from(batch, 0, &info->learned_delays, + /*resets_delays*/ r->reset_delays_wait >= 0, + r->reset_delays_wait); + if (result != ERROR_OK) + return result; + /* TODO: To use `riscv_batch_finished_scans()` here, it is needed for + * all scans to not discard input, meaning + * "riscv_batch_add_dm_write(..., false)" should not be used. */ + const size_t finished_scans = batch->used_scans; + decrement_reset_delays_counter(target, finished_scans); + if (riscv_batch_was_batch_busy(batch)) + return increase_dmi_busy_delay(target); + return ERROR_OK; +} + +/* It is expected that during creation of the batch + * "riscv_batch_add_dm_write(..., false)" was not used. + */ +static int batch_run_timeout(struct target *target, struct riscv_batch *batch) { RISCV013_INFO(info); - RISCV_INFO(r); - if (r->reset_delays_wait >= 0) { - r->reset_delays_wait -= batch->used_scans; - if (r->reset_delays_wait <= 0) { - batch->idle_count = 0; - info->dmi_busy_delay = 0; - info->ac_busy_delay = 0; + select_dmi(target->tap); + riscv_batch_add_nop(batch); + + size_t finished_scans = 0; + const time_t start = time(NULL); + const unsigned int old_base_delay = riscv_scan_get_delay(&info->learned_delays, + RISCV_DELAY_BASE); + int result; + do { + RISCV_INFO(r); + result = riscv_batch_run_from(batch, finished_scans, + &info->learned_delays, + /*resets_delays*/ r->reset_delays_wait >= 0, + r->reset_delays_wait); + if (result != ERROR_OK) + return result; + const size_t new_finished_scans = riscv_batch_finished_scans(batch); + assert(new_finished_scans >= finished_scans); + decrement_reset_delays_counter(target, new_finished_scans - finished_scans); + finished_scans = new_finished_scans; + if (!riscv_batch_was_batch_busy(batch)) { + assert(finished_scans == batch->used_scans); + return ERROR_OK; } - } - return riscv_batch_run(batch); + result = increase_dmi_busy_delay(target); + if (result != ERROR_OK) + return result; + } while (time(NULL) - start < riscv_get_command_timeout_sec()); + + assert(result == ERROR_OK); + assert(riscv_batch_was_batch_busy(batch)); + + /* Reset dmi_busy_delay, so the value doesn't get too big. */ + LOG_TARGET_DEBUG(target, "%s delay is restored to %u.", + riscv_scan_delay_class_name(RISCV_DELAY_BASE), + old_base_delay); + riscv_scan_set_delay(&info->learned_delays, RISCV_DELAY_BASE, + old_base_delay); + + LOG_TARGET_ERROR(target, "DMI operation didn't complete in %d seconds. " + "The target is either really slow or broken. You could increase " + "the timeout with riscv set_command_timeout_sec.", + riscv_get_command_timeout_sec()); + return ERROR_TIMEOUT_REACHED; } static int sba_supports_access(struct target *target, unsigned int size_bytes) @@ -2130,12 +2604,12 @@ static int sample_memory_bus_v1(struct target *target, RISCV013_INFO(info); unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); if (sbasize > 64) { - LOG_ERROR("Memory sampling is only implemented for sbasize <= 64."); + LOG_TARGET_ERROR(target, "Memory sampling is only implemented for sbasize <= 64."); return ERROR_NOT_IMPLEMENTED; } if (get_field(info->sbcs, DM_SBCS_SBVERSION) != 1) { - LOG_ERROR("Memory sampling is only implemented for SBA version 1."); + LOG_TARGET_ERROR(target, "Memory sampling is only implemented for SBA version 1."); return ERROR_NOT_IMPLEMENTED; } @@ -2163,8 +2637,7 @@ static int sample_memory_bus_v1(struct target *target, * loop. */ struct riscv_batch *batch = riscv_batch_alloc( - target, 1 + enabled_count * 5 * repeat, - info->dmi_busy_delay + info->bus_master_read_delay); + target, 1 + enabled_count * 5 * repeat); if (!batch) return ERROR_FAIL; @@ -2173,7 +2646,7 @@ static int sample_memory_bus_v1(struct target *target, for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) { if (config->bucket[i].enabled) { if (!sba_supports_access(target, config->bucket[i].size_bytes)) { - LOG_ERROR("Hardware does not support SBA access for %d-byte memory sampling.", + LOG_TARGET_ERROR(target, "Hardware does not support SBA access for %d-byte memory sampling.", config->bucket[i].size_bytes); return ERROR_NOT_IMPLEMENTED; } @@ -2183,7 +2656,8 @@ static int sample_memory_bus_v1(struct target *target, sbcs_write |= DM_SBCS_SBREADONDATA; sbcs_write |= sb_sbaccess(config->bucket[i].size_bytes); if (!sbcs_valid || sbcs_write != sbcs) { - riscv_batch_add_dmi_write(batch, DM_SBCS, sbcs_write); + riscv_batch_add_dm_write(batch, DM_SBCS, sbcs_write, + true, RISCV_DELAY_BASE); sbcs = sbcs_write; sbcs_valid = true; } @@ -2192,18 +2666,23 @@ static int sample_memory_bus_v1(struct target *target, (!sbaddress1_valid || sbaddress1 != config->bucket[i].address >> 32)) { sbaddress1 = config->bucket[i].address >> 32; - riscv_batch_add_dmi_write(batch, DM_SBADDRESS1, sbaddress1); + riscv_batch_add_dm_write(batch, DM_SBADDRESS1, + sbaddress1, true, RISCV_DELAY_BASE); sbaddress1_valid = true; } if (!sbaddress0_valid || sbaddress0 != (config->bucket[i].address & 0xffffffff)) { sbaddress0 = config->bucket[i].address; - riscv_batch_add_dmi_write(batch, DM_SBADDRESS0, sbaddress0); + riscv_batch_add_dm_write(batch, DM_SBADDRESS0, + sbaddress0, true, + RISCV_DELAY_SYSBUS_READ); sbaddress0_valid = true; } if (config->bucket[i].size_bytes > 4) - riscv_batch_add_dmi_read(batch, DM_SBDATA1); - riscv_batch_add_dmi_read(batch, DM_SBDATA0); + riscv_batch_add_dm_read(batch, DM_SBDATA1, + RISCV_DELAY_SYSBUS_READ); + riscv_batch_add_dm_read(batch, DM_SBDATA0, + RISCV_DELAY_SYSBUS_READ); result_bytes += 1 + config->bucket[i].size_bytes; } } @@ -2214,37 +2693,56 @@ static int sample_memory_bus_v1(struct target *target, break; } - size_t sbcs_key = riscv_batch_add_dmi_read(batch, DM_SBCS); + size_t sbcs_read_index = riscv_batch_add_dm_read(batch, DM_SBCS, + RISCV_DELAY_BASE); int result = batch_run(target, batch); - if (result != ERROR_OK) - return result; - - uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_key); - if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { - /* Discard this batch (too much hassle to try to recover partial - * data) and try again with a larger delay. */ - info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1; - dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); + if (result != ERROR_OK) { riscv_batch_free(batch); + return result; + } + + /* Discard the batch when we encounter a busy state on the DMI level. + * It's too much hassle to try to recover partial data. We'll try again + * with a larger DMI delay. */ + const uint32_t sbcs_read_op = riscv_batch_get_dmi_read_op(batch, sbcs_read_index); + if (sbcs_read_op == DTM_DMI_OP_BUSY) { + result = increase_dmi_busy_delay(target); + if (result != ERROR_OK) { + riscv_batch_free(batch); + return result; + } + continue; + } + + uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_read_index); + if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { + /* Discard this batch when we encounter "busy error" state on the System Bus level. + * We'll try next time with a larger System Bus read delay. */ + dm_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); + int res = riscv_scan_increase_delay(&info->learned_delays, + RISCV_DELAY_SYSBUS_READ); + riscv_batch_free(batch); + if (res != ERROR_OK) + return res; continue; } if (get_field(sbcs_read, DM_SBCS_SBERROR)) { /* The memory we're sampling was unreadable, somehow. Give up. */ - dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); + dm_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); riscv_batch_free(batch); return ERROR_FAIL; } - unsigned int read = 0; + unsigned int read_count = 0; for (unsigned int n = 0; n < repeat; n++) { for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) { if (config->bucket[i].enabled) { assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE); uint64_t value = 0; if (config->bucket[i].size_bytes > 4) - value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read++)) << 32; - value |= riscv_batch_get_dmi_read_data(batch, read++); + value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read_count++)) << 32; + value |= riscv_batch_get_dmi_read_data(batch, read_count++); buf->buf[buf->used] = i; buf_set_u64(buf->buf + buf->used + 1, 0, config->bucket[i].size_bytes * 8, value); @@ -2270,41 +2768,119 @@ static int sample_memory(struct target *target, return sample_memory_bus_v1(target, buf, config, until_ms); } +static int riscv013_get_hart_state(struct target *target, enum riscv_hart_state *state) +{ + RISCV013_INFO(info); + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + uint32_t dmstatus; + if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) + return ERROR_FAIL; + if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) { + LOG_TARGET_INFO(target, "Hart unexpectedly reset!"); + info->dcsr_ebreak_is_set = false; + /* TODO: Can we make this more obvious to eg. a gdb user? */ + uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | + DM_DMCONTROL_ACKHAVERESET; + dmcontrol = set_dmcontrol_hartsel(dmcontrol, info->index); + /* If we had been halted when we reset, request another halt. If we + * ended up running out of reset, then the user will (hopefully) get a + * message that a reset happened, that the target is running, and then + * that it is halted again once the request goes through. + */ + if (target->state == TARGET_HALTED) { + dmcontrol |= DM_DMCONTROL_HALTREQ; + /* `haltreq` should not be issued if `abstractcs.busy` + * is set. */ + int result = wait_for_idle_if_needed(target); + if (result != ERROR_OK) + return result; + } + dm_write(target, DM_DMCONTROL, dmcontrol); + } + if (get_field(dmstatus, DM_DMSTATUS_ALLNONEXISTENT)) { + *state = RISCV_STATE_NON_EXISTENT; + return ERROR_OK; + } + if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { + *state = RISCV_STATE_UNAVAILABLE; + return ERROR_OK; + } + if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED)) { + *state = RISCV_STATE_HALTED; + return ERROR_OK; + } + if (get_field(dmstatus, DM_DMSTATUS_ALLRUNNING)) { + *state = RISCV_STATE_RUNNING; + return ERROR_OK; + } + LOG_TARGET_ERROR(target, "Couldn't determine state. dmstatus=0x%x", dmstatus); + return ERROR_FAIL; +} + +static int handle_became_unavailable(struct target *target, + enum riscv_hart_state previous_riscv_state) +{ + RISCV013_INFO(info); + + if (riscv_reg_cache_any_dirty(target, LOG_LVL_WARNING)) + LOG_TARGET_WARNING(target, "Discarding values of dirty registers " + "(due to target becoming unavailable)."); + + riscv_reg_cache_invalidate_all(target); + + info->dcsr_ebreak_is_set = false; + return ERROR_OK; +} + +static int tick(struct target *target) +{ + RISCV013_INFO(info); + if (!info->dcsr_ebreak_is_set && + target->state == TARGET_RUNNING && + target_was_examined(target)) + return halt_set_dcsr_ebreak(target); + return ERROR_OK; +} + static int init_target(struct command_context *cmd_ctx, struct target *target) { - LOG_DEBUG("init"); + LOG_TARGET_DEBUG(target, "Init."); RISCV_INFO(generic_info); - generic_info->get_register = &riscv013_get_register; - generic_info->set_register = &riscv013_set_register; - generic_info->get_register_buf = &riscv013_get_register_buf; - generic_info->set_register_buf = &riscv013_set_register_buf; - generic_info->select_current_hart = &riscv013_select_current_hart; - generic_info->is_halted = &riscv013_is_halted; + generic_info->select_target = &dm013_select_target; + generic_info->get_hart_state = &riscv013_get_hart_state; generic_info->resume_go = &riscv013_resume_go; generic_info->step_current_hart = &riscv013_step_current_hart; - generic_info->on_halt = &riscv013_on_halt; generic_info->resume_prep = &riscv013_resume_prep; generic_info->halt_prep = &riscv013_halt_prep; generic_info->halt_go = &riscv013_halt_go; generic_info->on_step = &riscv013_on_step; generic_info->halt_reason = &riscv013_halt_reason; - generic_info->read_debug_buffer = &riscv013_read_debug_buffer; - generic_info->write_debug_buffer = &riscv013_write_debug_buffer; - generic_info->execute_debug_buffer = &riscv013_execute_debug_buffer; - generic_info->fill_dmi_write_u64 = &riscv013_fill_dmi_write_u64; - generic_info->fill_dmi_read_u64 = &riscv013_fill_dmi_read_u64; - generic_info->fill_dmi_nop_u64 = &riscv013_fill_dmi_nop_u64; - generic_info->dmi_write_u64_bits = &riscv013_dmi_write_u64_bits; + generic_info->read_progbuf = &riscv013_read_progbuf; + generic_info->write_progbuf = &riscv013_write_progbuf; + generic_info->execute_progbuf = &riscv013_execute_progbuf; + generic_info->invalidate_cached_progbuf = &riscv013_invalidate_cached_progbuf; + generic_info->fill_dmi_write = &riscv013_fill_dmi_write; + generic_info->fill_dmi_read = &riscv013_fill_dmi_read; + generic_info->fill_dm_nop = &riscv013_fill_dm_nop; + generic_info->get_dmi_address_bits = &riscv013_get_dmi_address_bits; generic_info->authdata_read = &riscv013_authdata_read; generic_info->authdata_write = &riscv013_authdata_write; generic_info->dmi_read = &dmi_read; generic_info->dmi_write = &dmi_write; - generic_info->read_memory = read_memory; - generic_info->hart_count = &riscv013_hart_count; + generic_info->get_dmi_address = &riscv013_get_dmi_address; + generic_info->access_memory = &riscv013_access_memory; generic_info->data_bits = &riscv013_data_bits; generic_info->print_info = &riscv013_print_info; + generic_info->get_impebreak = &riscv013_get_impebreak; + generic_info->get_progbufsize = &riscv013_get_progbufsize; + + generic_info->handle_became_unavailable = &handle_became_unavailable; + generic_info->tick = &tick; + if (!generic_info->version_specific) { generic_info->version_specific = calloc(1, sizeof(riscv013_info_t)); if (!generic_info->version_specific) @@ -2314,175 +2890,228 @@ static int init_target(struct command_context *cmd_ctx, riscv013_info_t *info = get_info(target); info->progbufsize = -1; + reset_learned_delays(target); - info->dmi_busy_delay = 0; - info->bus_master_read_delay = 0; - info->bus_master_write_delay = 0; - info->ac_busy_delay = 0; - - /* Assume all these abstract commands are supported until we learn - * otherwise. - * TODO: The spec allows eg. one CSR to be able to be accessed abstractly - * while another one isn't. We don't track that this closely here, but in - * the future we probably should. */ - info->abstract_read_csr_supported = true; - info->abstract_write_csr_supported = true; - info->abstract_read_fpr_supported = true; - info->abstract_write_fpr_supported = true; - - info->has_aampostincrement = YNM_MAYBE; + info->ac_not_supported_cache = ac_cache_construct(); return ERROR_OK; } static int assert_reset(struct target *target) { - RISCV_INFO(r); + RISCV013_INFO(info); + int result; - select_dmi(target); - - uint32_t control_base = set_field(0, DM_DMCONTROL_DMACTIVE, 1); + select_dmi(target->tap); if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) { /* Run the user-supplied script if there is one. */ target_handle_event(target, TARGET_EVENT_RESET_ASSERT); - } else if (target->rtos) { - /* There's only one target, and OpenOCD thinks each hart is a thread. - * We must reset them all. */ - - /* TODO: Try to use hasel in dmcontrol */ - - /* Set haltreq for each hart. */ - uint32_t control = set_hartsel(control_base, target->coreid); - control = set_field(control, DM_DMCONTROL_HALTREQ, - target->reset_halt ? 1 : 0); - dmi_write(target, DM_DMCONTROL, control); - - /* Assert ndmreset */ - control = set_field(control, DM_DMCONTROL_NDMRESET, 1); - dmi_write(target, DM_DMCONTROL, control); - } else { - /* Reset just this hart. */ - uint32_t control = set_hartsel(control_base, r->current_hartid); + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + + uint32_t control = set_field(0, DM_DMCONTROL_DMACTIVE, 1); + control = set_dmcontrol_hartsel(control, info->index); control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); control = set_field(control, DM_DMCONTROL_NDMRESET, 1); - dmi_write(target, DM_DMCONTROL, control); + /* If `abstractcs.busy` is set, debugger should not + * change `hartsel` or set `haltreq` + */ + const bool hartsel_changed = (int)info->index != dm->current_hartid; + if (hartsel_changed || target->reset_halt) { + result = wait_for_idle_if_needed(target); + if (result != ERROR_OK) + return result; + } + result = dm_write(target, DM_DMCONTROL, control); + if (result != ERROR_OK) + return result; } target->state = TARGET_RESET; - dm013_info_t *dm = get_dm(target); - if (!dm) - return ERROR_FAIL; - /* The DM might have gotten reset if OpenOCD called us in some reset that * involves SRST being toggled. So clear our cache which may be out of * date. */ - memset(dm->progbuf_cache, 0, sizeof(dm->progbuf_cache)); + return riscv013_invalidate_cached_progbuf(target); +} - return ERROR_OK; +static bool dcsr_ebreak_config_equals_reset_value(const struct target *target) +{ + const struct riscv_private_config * const config = riscv_private_config(target); + for (int i = 0; i < N_RISCV_MODE; ++i) + if (config->dcsr_ebreak_fields[i]) + return false; + return true; } static int deassert_reset(struct target *target) { - RISCV_INFO(r); RISCV013_INFO(info); - select_dmi(target); + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + int result; + select_dmi(target->tap); /* Clear the reset, but make sure haltreq is still set */ - uint32_t control = 0, control_haltreq; + uint32_t control = 0; control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); - control_haltreq = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); - dmi_write(target, DM_DMCONTROL, - set_hartsel(control_haltreq, r->current_hartid)); + control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); + control = set_dmcontrol_hartsel(control, info->index); + /* If `abstractcs.busy` is set, debugger should not + * change `hartsel`. + */ + const bool hartsel_changed = (int)info->index != dm->current_hartid; + if (hartsel_changed) { + result = wait_for_idle_if_needed(target); + if (result != ERROR_OK) + return result; + } + result = dm_write(target, DM_DMCONTROL, control); + if (result != ERROR_OK) + return result; uint32_t dmstatus; - int dmi_busy_delay = info->dmi_busy_delay; + const unsigned int orig_base_delay = riscv_scan_get_delay(&info->learned_delays, + RISCV_DELAY_BASE); time_t start = time(NULL); + LOG_TARGET_DEBUG(target, "Waiting for hart to come out of reset."); + do { + result = dmstatus_read(target, &dmstatus, true); + if (result != ERROR_OK) + return result; - for (int i = 0; i < riscv_count_harts(target); ++i) { - int index = i; - if (target->rtos) { - if (index != target->coreid) - continue; - dmi_write(target, DM_DMCONTROL, - set_hartsel(control_haltreq, index)); - } else { - index = r->current_hartid; + if (time(NULL) - start > riscv_get_command_timeout_sec()) { + LOG_TARGET_ERROR(target, "Hart didn't leave reset in %ds; " + "dmstatus=0x%x (allunavail=%s, allhavereset=%s); " + "Increase the timeout with riscv set_command_timeout_sec.", + riscv_get_command_timeout_sec(), dmstatus, + get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL) ? "true" : "false", + get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET) ? "true" : "false"); + return ERROR_TIMEOUT_REACHED; } + /* Certain debug modules, like the one in GD32VF103 + * MCUs, violate the specification's requirement that + * each hart is in "exactly one of four states" and, + * during reset, report harts as both unavailable and + * halted/running. To work around this, we check for + * the absence of the unavailable state rather than + * the presence of any other state. */ + } while (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL) && + !get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET)); - LOG_DEBUG("Waiting for hart %d to come out of reset.", index); - while (1) { - int result = dmstatus_read_timeout(target, &dmstatus, true, - riscv_reset_timeout_sec); - if (result == ERROR_TIMEOUT_REACHED) - LOG_ERROR("Hart %d didn't complete a DMI read coming out of " - "reset in %ds; Increase the timeout with riscv " - "set_reset_timeout_sec.", - index, riscv_reset_timeout_sec); - if (result != ERROR_OK) - return result; - /* Certain debug modules, like the one in GD32VF103 - * MCUs, violate the specification's requirement that - * each hart is in "exactly one of four states" and, - * during reset, report harts as both unavailable and - * halted/running. To work around this, we check for - * the absence of the unavailable state rather than - * the presence of any other state. */ - if (!get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) - break; - if (time(NULL) - start > riscv_reset_timeout_sec) { - LOG_ERROR("Hart %d didn't leave reset in %ds; " - "dmstatus=0x%x; " - "Increase the timeout with riscv set_reset_timeout_sec.", - index, riscv_reset_timeout_sec, dmstatus); - return ERROR_FAIL; - } - } + riscv_scan_set_delay(&info->learned_delays, RISCV_DELAY_BASE, + orig_base_delay); + + /* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */ + control = 0; + control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); + control = set_field(control, DM_DMCONTROL_ACKHAVERESET, 1); + control = set_dmcontrol_hartsel(control, info->index); + result = dm_write(target, DM_DMCONTROL, control); + if (result != ERROR_OK) + return result; + + if (target->reset_halt) { target->state = TARGET_HALTED; - - if (get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET)) { - /* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */ - dmi_write(target, DM_DMCONTROL, - set_hartsel(control, index) | - DM_DMCONTROL_ACKHAVERESET); - } - - if (!target->rtos) - break; + target->debug_reason = DBG_REASON_DBGRQ; + } else { + target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; } - info->dmi_busy_delay = dmi_busy_delay; + info->dcsr_ebreak_is_set = dcsr_ebreak_config_equals_reset_value(target); return ERROR_OK; } -static int execute_fence(struct target *target) +static int execute_autofence(struct target *target) { + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + RISCV_INFO(r); + if (!r->autofence) + return ERROR_OK; + /* FIXME: For non-coherent systems we need to flush the caches right * here, but there's no ISA-defined way of doing that. */ - { - struct riscv_program program; + struct riscv_program program; + + /* program.execution_result may indicate RISCV_PROGBUF_EXEC_RESULT_EXCEPTION - + * currently, we ignore this error since most likely this is an indication + * that target does not support a fence instruction (execution of an + * unsupported instruction results in "Illegal instruction" exception on + * targets that comply with riscv-privilege spec). + * Currently, RISC-V specification does not provide us with a portable and + * less invasive way to detect if a fence is supported by the target. We may + * revise this code once the spec allows us to do this */ + if (has_sufficient_progbuf(target, 3)) { riscv_program_init(&program, target); riscv_program_fence_i(&program); - riscv_program_fence(&program); - int result = riscv_program_exec(&program, target); - if (result != ERROR_OK) - LOG_DEBUG("Unable to execute pre-fence"); + riscv_program_fence_rw_rw(&program); + if (riscv_program_exec(&program, target) != ERROR_OK) { + if (program.execution_result != RISCV_PROGBUF_EXEC_RESULT_EXCEPTION) { + LOG_TARGET_ERROR(target, "Unexpected error during fence execution"); + return ERROR_FAIL; + } + LOG_TARGET_DEBUG(target, "Unable to execute fence.i and fence rw, rw"); + } + LOG_TARGET_DEBUG(target, "Successfully executed fence.i and fence rw, rw"); + return ERROR_OK; } - return ERROR_OK; + if (has_sufficient_progbuf(target, 2)) { + riscv_program_init(&program, target); + riscv_program_fence_i(&program); + if (riscv_program_exec(&program, target) != ERROR_OK) { + if (program.execution_result != RISCV_PROGBUF_EXEC_RESULT_EXCEPTION) { + LOG_TARGET_ERROR(target, "Unexpected error during fence.i execution"); + return ERROR_FAIL; + } + LOG_TARGET_DEBUG(target, "Unable to execute fence.i"); + } + LOG_TARGET_DEBUG(target, "Successfully executed fence.i"); + + riscv_program_init(&program, target); + riscv_program_fence_rw_rw(&program); + if (riscv_program_exec(&program, target) != ERROR_OK) { + if (program.execution_result != RISCV_PROGBUF_EXEC_RESULT_EXCEPTION) { + LOG_TARGET_ERROR(target, "Unexpected error during fence rw, rw execution"); + return ERROR_FAIL; + } + LOG_TARGET_DEBUG(target, "Unable to execute fence rw, rw"); + } + LOG_TARGET_DEBUG(target, "Successfully executed fence rw, rw"); + return ERROR_OK; + } + + return ERROR_FAIL; } -static void log_memory_access(target_addr_t address, uint64_t value, - unsigned int size_bytes, bool read) +static void log_memory_access128(target_addr_t address, uint64_t value_h, + uint64_t value_l, bool is_read) +{ + if (debug_level < LOG_LVL_DEBUG) + return; + + char fmt[80]; + sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%016" PRIx64 "%%016" PRIx64, + address, is_read ? "read" : "write"); + LOG_DEBUG(fmt, value_h, value_l); +} + +static void log_memory_access64(target_addr_t address, uint64_t value, + unsigned int size_bytes, bool is_read) { if (debug_level < LOG_LVL_DEBUG) return; char fmt[80]; sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%0%d" PRIx64, - address, read ? "read" : "write", size_bytes * 2); + address, is_read ? "read" : "write", size_bytes * 2); switch (size_bytes) { case 1: value &= 0xff; @@ -2500,23 +3129,35 @@ static void log_memory_access(target_addr_t address, uint64_t value, } LOG_DEBUG(fmt, value); } +static void log_memory_access(target_addr_t address, uint32_t *sbvalue, + unsigned int size_bytes, bool is_read) +{ + if (size_bytes == 16) { + uint64_t value_h = ((uint64_t)sbvalue[3] << 32) | sbvalue[2]; + uint64_t value_l = ((uint64_t)sbvalue[1] << 32) | sbvalue[0]; + log_memory_access128(address, value_h, value_l, is_read); + } else { + uint64_t value = ((uint64_t)sbvalue[1] << 32) | sbvalue[0]; + log_memory_access64(address, value, size_bytes, is_read); + } +} /* Read the relevant sbdata regs depending on size, and put the results into * buffer. */ static int read_memory_bus_word(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) { - uint32_t value; int result; + uint32_t sbvalue[4] = { 0 }; static int sbdata[4] = { DM_SBDATA0, DM_SBDATA1, DM_SBDATA2, DM_SBDATA3 }; assert(size <= 16); for (int i = (size - 1) / 4; i >= 0; i--) { - result = dmi_op(target, &value, NULL, DMI_OP_READ, sbdata[i], 0, false, true); + result = dm_read(target, &sbvalue[i], sbdata[i]); if (result != ERROR_OK) return result; - buf_set_u32(buffer + i * 4, 0, 8 * MIN(size, 4), value); - log_memory_access(address + i * 4, value, MIN(size, 4), true); + buf_set_u32(buffer + i * 4, 0, 8 * MIN(size, 4), sbvalue[i]); } + log_memory_access(address, sbvalue, size, true); return ERROR_OK; } @@ -2527,12 +3168,12 @@ static target_addr_t sb_read_address(struct target *target) target_addr_t address = 0; uint32_t v; if (sbasize > 32) { - dmi_read(target, &v, DM_SBADDRESS1); - address |= v; + if (dm_read(target, &v, DM_SBADDRESS1) == ERROR_OK) + address |= v; address <<= 32; } - dmi_read(target, &v, DM_SBADDRESS0); - address |= v; + if (dm_read(target, &v, DM_SBADDRESS0) == ERROR_OK) + address |= v; return address; } @@ -2540,63 +3181,99 @@ static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs) { time_t start = time(NULL); while (1) { - if (dmi_read(target, sbcs, DM_SBCS) != ERROR_OK) + if (dm_read(target, sbcs, DM_SBCS) != ERROR_OK) return ERROR_FAIL; if (!get_field(*sbcs, DM_SBCS_SBBUSY)) return ERROR_OK; - if (time(NULL) - start > riscv_command_timeout_sec) { - LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " + if (time(NULL) - start > riscv_get_command_timeout_sec()) { + LOG_TARGET_ERROR(target, "Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " "Increase the timeout with riscv set_command_timeout_sec.", - riscv_command_timeout_sec, *sbcs); + riscv_get_command_timeout_sec(), *sbcs); return ERROR_FAIL; } } } -static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t *mstatus_old) +/* TODO: return struct mem_access_result */ +static int modify_privilege_for_virt2phys_mode(struct target *target, riscv_reg_t *mstatus, riscv_reg_t *mstatus_old, + riscv_reg_t *dcsr, riscv_reg_t *dcsr_old) { - if (riscv_enable_virtual && has_sufficient_progbuf(target, 5)) { - /* Read DCSR */ - uint64_t dcsr; - if (register_read(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) - return ERROR_FAIL; + assert(mstatus); + assert(mstatus_old); + assert(dcsr); + assert(dcsr_old); + if (!riscv_virt2phys_mode_is_hw(target)) + return ERROR_OK; - /* Read and save MSTATUS */ - if (register_read(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) - return ERROR_FAIL; - *mstatus_old = *mstatus; + /* Read and save DCSR */ + if (riscv_reg_get(target, dcsr, GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + *dcsr_old = *dcsr; - /* If we come from m-mode with mprv set, we want to keep mpp */ - if (get_field(dcsr, DCSR_PRV) < 3) { - /* MPP = PRIV */ - *mstatus = set_field(*mstatus, MSTATUS_MPP, get_field(dcsr, DCSR_PRV)); + /* Read and save MSTATUS */ + if (riscv_reg_get(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) + return ERROR_FAIL; + *mstatus_old = *mstatus; - /* MPRV = 1 */ - *mstatus = set_field(*mstatus, MSTATUS_MPRV, 1); + /* If we come from m-mode with mprv set, we want to keep mpp */ + if (get_field(*dcsr, CSR_DCSR_PRV) == PRV_M) + return ERROR_OK; - /* Write MSTATUS */ - if (*mstatus != *mstatus_old) - if (register_write_direct(target, GDB_REGNO_MSTATUS, *mstatus) != ERROR_OK) - return ERROR_FAIL; - } - } + /* mstatus.mpp <- dcsr.prv */ + *mstatus = set_field(*mstatus, MSTATUS_MPP, get_field(*dcsr, CSR_DCSR_PRV)); + + /* mstatus.mprv <- 1 */ + *mstatus = set_field(*mstatus, MSTATUS_MPRV, 1); + + /* Write MSTATUS */ + if (*mstatus != *mstatus_old && + riscv_reg_set(target, GDB_REGNO_MSTATUS, *mstatus) != ERROR_OK) + return ERROR_FAIL; + + /* dcsr.mprven <- 1 */ + *dcsr = set_field(*dcsr, CSR_DCSR_MPRVEN, CSR_DCSR_MPRVEN_ENABLED); + + /* Write DCSR */ + if (*dcsr != *dcsr_old && + riscv_reg_set(target, GDB_REGNO_DCSR, *dcsr) != ERROR_OK) + return ERROR_FAIL; return ERROR_OK; } -static int read_memory_bus_v0(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +static int restore_privilege_from_virt2phys_mode(struct target *target, riscv_reg_t mstatus, riscv_reg_t mstatus_old, + riscv_reg_t dcsr, riscv_reg_t dcsr_old) { - if (size != increment) { - LOG_ERROR("sba v0 reads only support size==increment"); + if (!riscv_virt2phys_mode_is_hw(target)) + return ERROR_OK; + + /* Restore MSTATUS */ + if (mstatus != mstatus_old && + riscv_reg_set(target, GDB_REGNO_MSTATUS, mstatus_old) != ERROR_OK) + return ERROR_FAIL; + + /* Restore DCSR */ + if (dcsr != dcsr_old && + riscv_reg_set(target, GDB_REGNO_DCSR, dcsr_old) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int read_memory_bus_v0(struct target *target, const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_read(args)); + + if (args.size != args.increment) { + LOG_TARGET_ERROR(target, "sba v0 reads only support size==increment"); return ERROR_NOT_IMPLEMENTED; } - LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08" - TARGET_PRIxADDR, size, count, address); - uint8_t *t_buffer = buffer; - riscv_addr_t cur_addr = address; - riscv_addr_t fin_addr = address + (count * size); + LOG_TARGET_DEBUG(target, "System Bus Access: size: %d\tcount:%d\tstart address: 0x%08" + TARGET_PRIxADDR, args.size, args.count, args.address); + uint8_t *t_buffer = args.read_buffer; + riscv_addr_t cur_addr = args.address; + riscv_addr_t fin_addr = args.address + (args.count * args.size); uint32_t access = 0; const int DM_SBCS_SBSINGLEREAD_OFFSET = 20; @@ -2606,65 +3283,65 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address, const uint32_t DM_SBCS_SBAUTOREAD = (0x1U << DM_SBCS_SBAUTOREAD_OFFSET); /* ww favorise one off reading if there is an issue */ - if (count == 1) { - for (uint32_t i = 0; i < count; i++) { - if (dmi_read(target, &access, DM_SBCS) != ERROR_OK) + if (args.count == 1) { + for (uint32_t i = 0; i < args.count; i++) { + if (dm_read(target, &access, DM_SBCS) != ERROR_OK) return ERROR_FAIL; - dmi_write(target, DM_SBADDRESS0, cur_addr); - /* size/2 matching the bit access of the spec 0.13 */ - access = set_field(access, DM_SBCS_SBACCESS, size/2); + dm_write(target, DM_SBADDRESS0, cur_addr); + /* size/2 matching the bit sbaccess of the spec 0.13 */ + access = set_field(access, DM_SBCS_SBACCESS, args.size / 2); access = set_field(access, DM_SBCS_SBSINGLEREAD, 1); - LOG_DEBUG("\r\nread_memory: sab: access: 0x%08x", access); - dmi_write(target, DM_SBCS, access); + LOG_TARGET_DEBUG(target, "read_memory: sab: access: 0x%08x", access); + dm_write(target, DM_SBCS, access); /* 3) read */ uint32_t value; - if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) + if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK) return ERROR_FAIL; - LOG_DEBUG("\r\nread_memory: sab: value: 0x%08x", value); - buf_set_u32(t_buffer, 0, 8 * size, value); - t_buffer += size; - cur_addr += size; + LOG_TARGET_DEBUG(target, "read_memory: sab: value: 0x%08x", value); + buf_set_u32(t_buffer, 0, 8 * args.size, value); + t_buffer += args.size; + cur_addr += args.size; } return ERROR_OK; } /* has to be the same size if we want to read a block */ - LOG_DEBUG("reading block until final address 0x%" PRIx64, fin_addr); - if (dmi_read(target, &access, DM_SBCS) != ERROR_OK) + LOG_TARGET_DEBUG(target, "Reading block until final address 0x%" PRIx64, fin_addr); + if (dm_read(target, &access, DM_SBCS) != ERROR_OK) return ERROR_FAIL; /* set current address */ - dmi_write(target, DM_SBADDRESS0, cur_addr); + dm_write(target, DM_SBADDRESS0, cur_addr); /* 2) write sbaccess=2, sbsingleread,sbautoread,sbautoincrement * size/2 matching the bit access of the spec 0.13 */ - access = set_field(access, DM_SBCS_SBACCESS, size/2); + access = set_field(access, DM_SBCS_SBACCESS, args.size / 2); access = set_field(access, DM_SBCS_SBAUTOREAD, 1); access = set_field(access, DM_SBCS_SBSINGLEREAD, 1); access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 1); - LOG_DEBUG("\r\naccess: 0x%08x", access); - dmi_write(target, DM_SBCS, access); + LOG_TARGET_DEBUG(target, "access: 0x%08x", access); + dm_write(target, DM_SBCS, access); while (cur_addr < fin_addr) { - LOG_DEBUG("\r\nsab:autoincrement: \r\n size: %d\tcount:%d\taddress: 0x%08" - PRIx64, size, count, cur_addr); + LOG_TARGET_DEBUG(target, "sab:autoincrement:\r\n\tsize: %d\tcount:%d\taddress: 0x%08" + PRIx64, args.size, args.count, cur_addr); /* read */ uint32_t value; - if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) + if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK) return ERROR_FAIL; - buf_set_u32(t_buffer, 0, 8 * size, value); - cur_addr += size; - t_buffer += size; + buf_set_u32(t_buffer, 0, 8 * args.size, value); + cur_addr += args.size; + t_buffer += args.size; /* if we are reaching last address, we must clear autoread */ - if (cur_addr == fin_addr && count != 1) { - dmi_write(target, DM_SBCS, 0); - if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) + if (cur_addr == fin_addr && args.count != 1) { + dm_write(target, DM_SBCS, 0); + if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK) return ERROR_FAIL; - buf_set_u32(t_buffer, 0, 8 * size, value); + buf_set_u32(t_buffer, 0, 8 * args.size, value); } } uint32_t sbcs; - if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK) + if (dm_read(target, &sbcs, DM_SBCS) != ERROR_OK) return ERROR_FAIL; return ERROR_OK; @@ -2673,18 +3350,35 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address, /** * Read the requested memory using the system bus interface. */ -static int read_memory_bus_v1(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +static int read_memory_bus_v1(struct target *target, const struct riscv_mem_access_args args) { + assert(riscv_mem_access_is_read(args)); + + const target_addr_t address = args.address; + const uint32_t increment = args.increment; + const uint32_t count = args.count; + const uint32_t size = args.size; + uint8_t *buffer = args.read_buffer; + if (increment != size && increment != 0) { - LOG_ERROR("sba v1 reads only support increment of size or 0"); + LOG_TARGET_ERROR(target, "sba v1 reads only support increment of size or 0"); return ERROR_NOT_IMPLEMENTED; } + assert(size <= 16); + assert(IS_PWR_OF_2(size)); + + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + RISCV013_INFO(info); target_addr_t next_address = address; - target_addr_t end_address = address + count * size; + target_addr_t end_address = address + (increment ? count : 1) * size; + /* TODO: Reading all the elements in a single batch will boost the + * performance. + */ while (next_address < end_address) { uint32_t sbcs_write = set_field(0, DM_SBCS_SBREADONADDR, 1); sbcs_write |= sb_sbaccess(size); @@ -2692,90 +3386,68 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, sbcs_write = set_field(sbcs_write, DM_SBCS_SBAUTOINCREMENT, 1); if (count > 1) sbcs_write = set_field(sbcs_write, DM_SBCS_SBREADONDATA, count > 1); - if (dmi_write(target, DM_SBCS, sbcs_write) != ERROR_OK) + if (dm_write(target, DM_SBCS, sbcs_write) != ERROR_OK) return ERROR_FAIL; /* This address write will trigger the first read. */ - if (sb_write_address(target, next_address, true) != ERROR_OK) + if (sb_write_address(target, next_address, RISCV_DELAY_SYSBUS_READ) != ERROR_OK) return ERROR_FAIL; - if (info->bus_master_read_delay) { - jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE); - if (jtag_execute_queue() != ERROR_OK) { - LOG_ERROR("Failed to scan idle sequence"); - return ERROR_FAIL; - } - } - - /* First value has been read, and is waiting for us to issue a DMI read - * to get it. */ + /* First read has been started. Optimistically assume that it has + * completed. */ static int sbdata[4] = {DM_SBDATA0, DM_SBDATA1, DM_SBDATA2, DM_SBDATA3}; - assert(size <= 16); - target_addr_t next_read = address - 1; + /* TODO: The only purpose of "sbvalue" is to be passed to + * "log_memory_access()". If "log_memory_access()" were to + * accept "uint8_t *" instead of "uint32_t *", "sbvalue" would + * be unnecessary. + */ + uint32_t sbvalue[4] = {0}; for (uint32_t i = (next_address - address) / size; i < count - 1; i++) { - for (int j = (size - 1) / 4; j >= 0; j--) { - uint32_t value; - unsigned int attempt = 0; - while (1) { - if (attempt++ > 100) { - LOG_ERROR("DMI keeps being busy in while reading memory just past " TARGET_ADDR_FMT, - next_read); - return ERROR_FAIL; - } - keep_alive(); - dmi_status_t status = dmi_scan(target, NULL, &value, - DMI_OP_READ, sbdata[j], 0, false); - if (status == DMI_STATUS_BUSY) - increase_dmi_busy_delay(target); - else if (status == DMI_STATUS_SUCCESS) - break; - else - return ERROR_FAIL; - } - if (next_read != address - 1) { - buf_set_u32(buffer + next_read - address, 0, 8 * MIN(size, 4), value); - log_memory_access(next_read, value, MIN(size, 4), true); - } - next_read = address + i * size + j * 4; + const uint32_t size_in_words = DIV_ROUND_UP(size, 4); + struct riscv_batch *batch = riscv_batch_alloc(target, size_in_words); + /* Read of sbdata0 must be performed as last because it + * starts the new bus data transfer + * (in case "sbcs.sbreadondata" was set above). + * We don't want to start the next bus read before we + * fetch all the data from the last bus read. */ + for (uint32_t j = size_in_words - 1; j > 0; --j) + riscv_batch_add_dm_read(batch, sbdata[j], RISCV_DELAY_BASE); + riscv_batch_add_dm_read(batch, sbdata[0], RISCV_DELAY_SYSBUS_READ); + + int res = batch_run_timeout(target, batch); + if (res != ERROR_OK) { + riscv_batch_free(batch); + return res; } + + const size_t last_key = batch->read_keys_used - 1; + for (size_t k = 0; k <= last_key; ++k) { + sbvalue[k] = riscv_batch_get_dmi_read_data(batch, last_key - k); + buf_set_u32(buffer + i * size + k * 4, 0, MIN(32, 8 * size), sbvalue[k]); + } + + riscv_batch_free(batch); + const target_addr_t read_addr = address + i * increment; + log_memory_access(read_addr, sbvalue, size, true); } uint32_t sbcs_read = 0; if (count > 1) { - uint32_t value; - unsigned int attempt = 0; - while (1) { - if (attempt++ > 100) { - LOG_ERROR("DMI keeps being busy in while reading memory just past " TARGET_ADDR_FMT, - next_read); - return ERROR_FAIL; - } - dmi_status_t status = dmi_scan(target, NULL, &value, DMI_OP_NOP, 0, 0, false); - if (status == DMI_STATUS_BUSY) - increase_dmi_busy_delay(target); - else if (status == DMI_STATUS_SUCCESS) - break; - else - return ERROR_FAIL; - } - buf_set_u32(buffer + next_read - address, 0, 8 * MIN(size, 4), value); - log_memory_access(next_read, value, MIN(size, 4), true); - /* "Writes to sbcs while sbbusy is high result in undefined behavior. * A debugger must not write to sbcs until it reads sbbusy as 0." */ if (read_sbcs_nonbusy(target, &sbcs_read) != ERROR_OK) return ERROR_FAIL; sbcs_write = set_field(sbcs_write, DM_SBCS_SBREADONDATA, 0); - if (dmi_write(target, DM_SBCS, sbcs_write) != ERROR_OK) + if (dm_write(target, DM_SBCS, sbcs_write) != ERROR_OK) return ERROR_FAIL; } /* Read the last word, after we disabled sbreadondata if necessary. */ if (!get_field(sbcs_read, DM_SBCS_SBERROR) && !get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { - if (read_memory_bus_word(target, address + (count - 1) * size, size, + if (read_memory_bus_word(target, address + (count - 1) * increment, size, buffer + (count - 1) * size) != ERROR_OK) return ERROR_FAIL; @@ -2784,21 +3456,38 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, } if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { - /* We read while the target was busy. Slow down and try again. */ - if (dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR) != ERROR_OK) + /* We read while the target was busy. Slow down and try again. + * Clear sbbusyerror, as well as readondata or readonaddr. */ + if (dm_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR) != ERROR_OK) return ERROR_FAIL; - next_address = sb_read_address(target); - info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1; + + if (get_field(sbcs_read, DM_SBCS_SBERROR) == DM_SBCS_SBERROR_NONE) { + /* Read the address whose read was last completed. */ + next_address = sb_read_address(target); + + /* Read the value for the last address. It's + * sitting in the register for us, but we read it + * too early (sbbusyerror became set). */ + target_addr_t current_address = next_address - (increment ? size : 0); + if (read_memory_bus_word(target, current_address, size, + buffer + current_address - address) != ERROR_OK) + return ERROR_FAIL; + } + + int res = riscv_scan_increase_delay(&info->learned_delays, + RISCV_DELAY_SYSBUS_READ); + if (res != ERROR_OK) + return res; continue; } unsigned int error = get_field(sbcs_read, DM_SBCS_SBERROR); - if (error == 0) { + if (error == DM_SBCS_SBERROR_NONE) { next_address = end_address; } else { /* Some error indicating the bus access failed, but not because of * something we did wrong. */ - if (dmi_write(target, DM_SBCS, DM_SBCS_SBERROR) != ERROR_OK) + if (dm_write(target, DM_SBCS, DM_SBCS_SBERROR) != ERROR_OK) return ERROR_FAIL; return ERROR_FAIL; } @@ -2807,7 +3496,8 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, return ERROR_OK; } -static void log_mem_access_result(struct target *target, bool success, int method, bool read) +static void log_mem_access_result(struct target *target, bool success, + enum riscv_mem_access_method method, bool is_read) { RISCV_INFO(r); bool warn = false; @@ -2816,127 +3506,271 @@ static void log_mem_access_result(struct target *target, bool success, int metho /* Compose the message */ snprintf(msg, 60, "%s to %s memory via %s.", success ? "Succeeded" : "Failed", - read ? "read" : "write", + is_read ? "read" : "write", (method == RISCV_MEM_ACCESS_PROGBUF) ? "program buffer" : (method == RISCV_MEM_ACCESS_SYSBUS) ? "system bus" : "abstract access"); /* Determine the log message severity. Show warnings only once. */ if (!success) { - if (method == RISCV_MEM_ACCESS_PROGBUF) { - warn = r->mem_access_progbuf_warn; - r->mem_access_progbuf_warn = false; - } - if (method == RISCV_MEM_ACCESS_SYSBUS) { - warn = r->mem_access_sysbus_warn; - r->mem_access_sysbus_warn = false; - } - if (method == RISCV_MEM_ACCESS_ABSTRACT) { - warn = r->mem_access_abstract_warn; - r->mem_access_abstract_warn = false; - } + warn = r->mem_access_warn[method]; + r->mem_access_warn[method] = false; } if (warn) - LOG_WARNING("%s", msg); + LOG_TARGET_WARNING(target, "%s", msg); else - LOG_DEBUG("%s", msg); + LOG_TARGET_DEBUG(target, "%s", msg); } -static bool mem_should_skip_progbuf(struct target *target, target_addr_t address, - uint32_t size, bool read, char **skip_reason) -{ - assert(skip_reason); +enum mem_access_result_type { + MEM_ACCESS_RESULT_TYPE_OK, + MEM_ACCESS_RESULT_TYPE_DISABLED, + MEM_ACCESS_RESULT_TYPE_SKIPPED, + MEM_ACCESS_RESULT_TYPE_FAILED, + MEM_ACCESS_RESULT_TYPE_ENUM_SIZE, +}; +#define LIST_OF_MEM_ACCESS_RESULTS \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_OK, OK, "ok") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_DISABLED, DISABLED, "disabled") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED, SKIPPED, "skipped") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_ABSTRACT_ACCESS_CMDERR, \ + SKIPPED, "skipped (abstract access cmderr)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_PROGBUF_NOT_PRESENT, \ + SKIPPED, "skipped (progbuf not present)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_PROGBUF_INSUFFICIENT, \ + SKIPPED, "skipped (insufficient progbuf)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_UNSUPPORTED_ACCESS_SIZE, \ + SKIPPED, "skipped (unsupported access size)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_XLEN_TOO_SHORT, \ + SKIPPED, "skipped (xlen too short)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_TARGET_NOT_HALTED, \ + SKIPPED, "skipped (target not halted)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_TOO_LARGE_ADDRESS, \ + SKIPPED, "skipped (address too large)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_UNSUPPORTED_INCREMENT_SIZE, \ + SKIPPED, "skipped (increment size not supported)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_TARGET_SELECT_FAILED, \ + SKIPPED, "skipped (dm target select failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_FENCE_EXEC_FAILED, \ + SKIPPED, "skipped (fence execution failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_SYSBUS_ACCESS_FAILED, \ + SKIPPED, "skipped (sysbus access failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_REG_SAVE_FAILED, \ + SKIPPED, "skipped (register save failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_UNKNOWN_SYSBUS_VERSION, \ + SKIPPED, "skipped (unknown sysbus version)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_PROGRAM_WRITE_FAILED, \ + SKIPPED, "skipped (program write failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_PROGBUF_FILL_FAILED, \ + SKIPPED, "skipped (progbuf fill failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_WRITE_ABSTRACT_ARG_FAILED, \ + SKIPPED, "skipped (abstract command argument write failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_SKIPPED_PRIV_MOD_FAILED, \ + SKIPPED, "skipped (privilege modification failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED, FAILED, "failed") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED_DM_ACCESS_FAILED, \ + FAILED, "failed (DM register access failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED_PRIV_MOD_FAILED, \ + FAILED, "failed (privilege modification failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED_REG_READ_FAILED, \ + FAILED, "failed (register read failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED_PROGBUF_STARTUP_FAILED, \ + FAILED, "failed (progbuf startup failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED_PROGBUF_INNER_FAILED, \ + FAILED, "failed (progbuf inner failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED_PROGBUF_TEARDOWN_FAILED, \ + FAILED, "failed (progbuf teardown failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED_EXECUTE_ABSTRACT_FAILED, \ + FAILED, "failed (execute abstract failed)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED_NO_FORWARD_PROGRESS, \ + FAILED, "failed (no forward progress)") \ + MEM_ACCESS_RESULT_HANDLER(MEM_ACCESS_FAILED_FENCE_EXEC_FAILED, \ + FAILED, "failed (fence execution failed)") \ + + +#define MEM_ACCESS_RESULT_HANDLER(name, kind, msg) name, +enum mem_access_result_enum { + LIST_OF_MEM_ACCESS_RESULTS +}; +#undef MEM_ACCESS_RESULT_HANDLER + +/* Structure is intentionally used to contain the memory access result, + for type safety - to avoid implicit conversions to integers. */ +struct mem_access_result { + enum mem_access_result_enum value; +}; + +bool is_mem_access_ok(struct mem_access_result status) +{ + #define MEM_ACCESS_RESULT_HANDLER(name, kind, msg) \ + case name: return MEM_ACCESS_RESULT_TYPE_##kind \ + == MEM_ACCESS_RESULT_TYPE_OK; + + switch (status.value) { + LIST_OF_MEM_ACCESS_RESULTS + } + #undef MEM_ACCESS_RESULT_HANDLER + + LOG_ERROR("Unknown memory access status: %d", status.value); + assert(false && "Unknown memory access status"); + return false; +} + +bool is_mem_access_failed(struct mem_access_result status) +{ + #define MEM_ACCESS_RESULT_HANDLER(name, kind, msg) \ + case name: return MEM_ACCESS_RESULT_TYPE_##kind \ + == MEM_ACCESS_RESULT_TYPE_FAILED; + + switch (status.value) { + LIST_OF_MEM_ACCESS_RESULTS + } + #undef MEM_ACCESS_RESULT_HANDLER + + LOG_ERROR("Unknown memory access status: %d", status.value); + assert(false && "Unknown memory access status"); + return true; +} + +bool is_mem_access_skipped(struct mem_access_result status) +{ + #define MEM_ACCESS_RESULT_HANDLER(name, kind, msg) \ + case name: return MEM_ACCESS_RESULT_TYPE_##kind \ + == MEM_ACCESS_RESULT_TYPE_SKIPPED; + + switch (status.value) { + LIST_OF_MEM_ACCESS_RESULTS + } + #undef MEM_ACCESS_RESULT_HANDLER + LOG_ERROR("Unknown memory access status: %d", status.value); + assert(false && "Unknown memory access status"); + return true; +} + +const char *mem_access_result_to_str(struct mem_access_result status) +{ + #define MEM_ACCESS_RESULT_HANDLER(name, kind, msg) \ + [name] = msg, + static const char * const table[] = { + LIST_OF_MEM_ACCESS_RESULTS + }; + #undef MEM_ACCESS_RESULT_HANDLER + + assert(status.value < ARRAY_SIZE(table)); + return table[status.value]; +} + +static struct mem_access_result mem_access_result(enum mem_access_result_enum value) +{ + struct mem_access_result result = {.value = value}; + return result; +} + +static struct mem_access_result mem_should_skip_progbuf(struct target *target, + const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_valid(args)); + const char *const access_type = + riscv_mem_access_is_read(args) ? "read" : "write"; + + if (!has_sufficient_progbuf(target, 1)) { + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf " + "- progbuf not present", access_type); + return mem_access_result(MEM_ACCESS_SKIPPED_PROGBUF_NOT_PRESENT); + } if (!has_sufficient_progbuf(target, 3)) { - LOG_DEBUG("Skipping mem %s via progbuf - insufficient progbuf size.", - read ? "read" : "write"); - *skip_reason = "skipped (insufficient progbuf)"; - return true; + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - " + "insufficient progbuf size.", access_type); + return mem_access_result(MEM_ACCESS_SKIPPED_PROGBUF_INSUFFICIENT); } if (target->state != TARGET_HALTED) { - LOG_DEBUG("Skipping mem %s via progbuf - target not halted.", - read ? "read" : "write"); - *skip_reason = "skipped (target not halted)"; - return true; + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - " + "target not halted.", access_type); + return mem_access_result(MEM_ACCESS_SKIPPED_TARGET_NOT_HALTED); } - if (riscv_xlen(target) < size * 8) { - LOG_DEBUG("Skipping mem %s via progbuf - XLEN (%d) is too short for %d-bit memory access.", - read ? "read" : "write", riscv_xlen(target), size * 8); - *skip_reason = "skipped (XLEN too short)"; - return true; + if (riscv_xlen(target) < args.size * 8) { + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - " + "XLEN (%d) is too short for %d-bit memory args.", + access_type, riscv_xlen(target), args.size * 8); + return mem_access_result(MEM_ACCESS_SKIPPED_XLEN_TOO_SHORT); } - if (size > 8) { - LOG_DEBUG("Skipping mem %s via progbuf - unsupported size.", - read ? "read" : "write"); - *skip_reason = "skipped (unsupported size)"; - return true; + if (args.size > 8) { + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - " + "unsupported size.", access_type); + return mem_access_result(MEM_ACCESS_SKIPPED_UNSUPPORTED_ACCESS_SIZE); } - if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) { - LOG_DEBUG("Skipping mem %s via progbuf - progbuf only supports %u-bit address.", - read ? "read" : "write", riscv_xlen(target)); - *skip_reason = "skipped (too large address)"; - return true; + if ((sizeof(args.address) * 8 > riscv_xlen(target)) + && (args.address >> riscv_xlen(target))) { + LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - " + "progbuf only supports %u-bit address.", access_type, riscv_xlen(target)); + return mem_access_result(MEM_ACCESS_SKIPPED_TOO_LARGE_ADDRESS); } - return false; + return mem_access_result(MEM_ACCESS_OK); } -static bool mem_should_skip_sysbus(struct target *target, target_addr_t address, - uint32_t size, uint32_t increment, bool read, char **skip_reason) +static struct mem_access_result +mem_should_skip_sysbus(struct target *target, const struct riscv_mem_access_args args) { - assert(skip_reason); + assert(riscv_mem_access_is_valid(args)); RISCV013_INFO(info); - if (!sba_supports_access(target, size)) { - LOG_DEBUG("Skipping mem %s via system bus - unsupported size.", - read ? "read" : "write"); - *skip_reason = "skipped (unsupported size)"; - return true; + const bool is_read = riscv_mem_access_is_read(args); + const char *const access_type = is_read ? "read" : "write"; + + if (!sba_supports_access(target, args.size)) { + LOG_TARGET_DEBUG(target, "Skipping mem %s via system bus - " + "unsupported size.", access_type); + return mem_access_result(MEM_ACCESS_SKIPPED_UNSUPPORTED_ACCESS_SIZE); } unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); - if ((sizeof(address) * 8 > sbasize) && (address >> sbasize)) { - LOG_DEBUG("Skipping mem %s via system bus - sba only supports %u-bit address.", - read ? "read" : "write", sbasize); - *skip_reason = "skipped (too large address)"; - return true; + if ((sizeof(args.address) * 8 > sbasize) + && (args.address >> sbasize)) { + LOG_TARGET_DEBUG(target, "Skipping mem %s via system bus - " + "sba only supports %u-bit address.", access_type, sbasize); + return mem_access_result(MEM_ACCESS_SKIPPED_TOO_LARGE_ADDRESS); } - if (read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) { - LOG_DEBUG("Skipping mem read via system bus - " - "sba reads only support size==increment or also size==0 for sba v1."); - *skip_reason = "skipped (unsupported increment)"; - return true; + if (is_read && args.increment != args.size + && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 + || args.increment != 0)) { + LOG_TARGET_DEBUG(target, "Skipping mem %s via system bus - " + "sba %ss only support (size == increment) or also " + "size==0 for sba v1.", access_type, access_type); + return mem_access_result(MEM_ACCESS_SKIPPED_UNSUPPORTED_INCREMENT_SIZE); } - return false; + return mem_access_result(MEM_ACCESS_OK); } -static bool mem_should_skip_abstract(struct target *target, target_addr_t address, - uint32_t size, uint32_t increment, bool read, char **skip_reason) +static struct mem_access_result +mem_should_skip_abstract(struct target *target, const struct riscv_mem_access_args args) { - assert(skip_reason); + assert(riscv_mem_access_is_valid(args)); - if (size > 8) { + const bool is_read = riscv_mem_access_is_read(args); + const char *const access_type = is_read ? "read" : "write"; + if (args.size > 8) { /* TODO: Add 128b support if it's ever used. Involves modifying read/write_abstract_arg() to work on two 64b values. */ - LOG_DEBUG("Skipping mem %s via abstract access - unsupported size: %d bits", - read ? "read" : "write", size * 8); - *skip_reason = "skipped (unsupported size)"; - return true; + LOG_TARGET_DEBUG(target, "Skipping mem %s via abstract access - " + "unsupported size: %d bits", access_type, args.size * 8); + return mem_access_result(MEM_ACCESS_SKIPPED_UNSUPPORTED_ACCESS_SIZE); } - if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) { - LOG_DEBUG("Skipping mem %s via abstract access - abstract access only supports %u-bit address.", - read ? "read" : "write", riscv_xlen(target)); - *skip_reason = "skipped (too large address)"; - return true; + if ((sizeof(args.address) * 8 > riscv_xlen(target)) + && (args.address >> riscv_xlen(target))) { + LOG_TARGET_DEBUG(target, "Skipping mem %s via abstract access - " + "abstract access only supports %u-bit address.", + access_type, riscv_xlen(target)); + return mem_access_result(MEM_ACCESS_SKIPPED_TOO_LARGE_ADDRESS); } - if (read && size != increment) { - LOG_ERROR("Skipping mem read via abstract access - " - "abstract command reads only support size==increment."); - *skip_reason = "skipped (unsupported increment)"; - return true; + if (is_read && args.size != args.increment) { + LOG_TARGET_ERROR(target, "Skipping mem %s via abstract access - " + "abstract command %ss only support (size == increment).", + access_type, access_type); + return mem_access_result(MEM_ACCESS_SKIPPED_UNSUPPORTED_INCREMENT_SIZE); } - - return false; + return mem_access_result(MEM_ACCESS_OK); } /* @@ -2944,78 +3778,76 @@ static bool mem_should_skip_abstract(struct target *target, target_addr_t addres * supported are 1, 2, and 4 bytes despite the spec's support of 8 and 16 byte * aamsize fields in the memory access abstract command. */ -static int read_memory_abstract(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +static struct mem_access_result +read_memory_abstract(struct target *target, const struct riscv_mem_access_args args) { - RISCV013_INFO(info); + assert(riscv_mem_access_is_read(args)); - int result = ERROR_OK; - bool use_aampostincrement = info->has_aampostincrement != YNM_NO; - - LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, - size, address); - - memset(buffer, 0, count * size); + memset(args.read_buffer, 0, args.count * args.size); /* Convert the size (bytes) to width (bits) */ - unsigned int width = size << 3; + unsigned int width = args.size << 3; - /* Create the command (physical address, postincrement, read) */ - uint32_t command = access_memory_command(target, false, width, use_aampostincrement, false); + uint32_t command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ true, /* is_write = */ false); + bool use_aampostincrement = !is_command_unsupported(target, command); + if (!use_aampostincrement) + /* It is already known that this abstract memory + * access with aampostincrement=1 is not supported. + * So try aampostincrement=0 right away. + * + * TODO: check if new command is supported */ + command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ false, /* is_write = */ false); /* Execute the reads */ - uint8_t *p = buffer; + uint8_t *p = args.read_buffer; + int result = ERROR_OK; bool updateaddr = true; - unsigned int width32 = (width < 32) ? 32 : width; - for (uint32_t c = 0; c < count; c++) { + unsigned int width32 = MAX(width, 32); + for (uint32_t c = 0; c < args.count; c++) { /* Update the address if it is the first time or aampostincrement is not supported by the target. */ if (updateaddr) { /* Set arg1 to the address: address + c * size */ - result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target)); + result = write_abstract_arg(target, 1, args.address + c * args.size, riscv_xlen(target)); if (result != ERROR_OK) { - LOG_ERROR("Failed to write arg1 during read_memory_abstract()."); - return result; + LOG_TARGET_ERROR(target, "Failed to write arg1."); + return mem_access_result(MEM_ACCESS_FAILED_DM_ACCESS_FAILED); } } /* Execute the command */ - result = execute_abstract_command(target, command); - - if (info->has_aampostincrement == YNM_MAYBE) { - if (result == ERROR_OK) { - /* Safety: double-check that the address was really auto-incremented */ - riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target)); - if (new_address == address + size) { - LOG_DEBUG("aampostincrement is supported on this target."); - info->has_aampostincrement = YNM_YES; - } else { - LOG_WARNING("Buggy aampostincrement! Address not incremented correctly."); - info->has_aampostincrement = YNM_NO; - } - } else { - /* Try the same access but with postincrement disabled. */ - command = access_memory_command(target, false, width, false, false); - result = execute_abstract_command(target, command); - if (result == ERROR_OK) { - LOG_DEBUG("aampostincrement is not supported on this target."); - info->has_aampostincrement = YNM_NO; - } - } + uint32_t cmderr; + result = riscv013_execute_abstract_command(target, command, &cmderr); + if (use_aampostincrement && result != ERROR_OK && + cmderr == CMDERR_NOT_SUPPORTED) { + LOG_TARGET_DEBUG(target, "Trying the same abstract memory " + "read command, but without aampostincrement"); + use_aampostincrement = false; + command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ false, /* is_write = */ false); + result = riscv013_execute_abstract_command(target, command, &cmderr); } + /* TODO: + * (1) Only the 1st access can result in a 'skip' + * (2) Analyze cmderr value */ if (result != ERROR_OK) - return result; + return mem_access_result(MEM_ACCESS_SKIPPED_ABSTRACT_ACCESS_CMDERR); /* Copy arg0 to buffer (rounded width up to nearest 32) */ - riscv_reg_t value = read_abstract_arg(target, 0, width32); - buf_set_u64(p, 0, 8 * size, value); + riscv_reg_t value; + result = read_abstract_arg(target, &value, 0, width32); + if (result != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED_DM_ACCESS_FAILED); + buf_set_u64(p, 0, 8 * args.size, value); - if (info->has_aampostincrement == YNM_YES) + if (use_aampostincrement) updateaddr = false; - p += size; + p += args.size; } - return result; + return mem_access_result(MEM_ACCESS_OK); } /* @@ -3023,735 +3855,920 @@ static int read_memory_abstract(struct target *target, target_addr_t address, * sizes supported are 1, 2, and 4 bytes despite the spec's support of 8 and 16 * byte aamsize fields in the memory access abstract command. */ -static int write_memory_abstract(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) +static struct mem_access_result +write_memory_abstract(struct target *target, const struct riscv_mem_access_args args) { - RISCV013_INFO(info); - int result = ERROR_OK; - bool use_aampostincrement = info->has_aampostincrement != YNM_NO; + assert(riscv_mem_access_is_write(args)); - LOG_DEBUG("writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, - size, address); + int result = ERROR_OK; /* Convert the size (bytes) to width (bits) */ - unsigned int width = size << 3; + unsigned int width = args.size << 3; - /* Create the command (physical address, postincrement, write) */ - uint32_t command = access_memory_command(target, false, width, use_aampostincrement, true); + uint32_t command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ true, /* is_write = */ true); + bool use_aampostincrement = !is_command_unsupported(target, command); + if (!use_aampostincrement) + /* It is already known that this abstract memory + * access with aampostincrement=1 is not supported. + * So try aampostincrement=0 right away. + * + * TODO: check if new command is supported */ + command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ false, /* is_write = */ true); /* Execute the writes */ - const uint8_t *p = buffer; + const uint8_t *p = args.write_buffer; bool updateaddr = true; - for (uint32_t c = 0; c < count; c++) { + for (uint32_t c = 0; c < args.count; c++) { /* Move data to arg0 */ - riscv_reg_t value = buf_get_u64(p, 0, 8 * size); + riscv_reg_t value = buf_get_u64(p, 0, 8 * args.size); result = write_abstract_arg(target, 0, value, riscv_xlen(target)); if (result != ERROR_OK) { - LOG_ERROR("Failed to write arg0 during write_memory_abstract()."); - return result; + LOG_TARGET_ERROR(target, "Failed to write arg0."); + return mem_access_result(MEM_ACCESS_FAILED_DM_ACCESS_FAILED); } /* Update the address if it is the first time or aampostincrement is not supported by the target. */ if (updateaddr) { /* Set arg1 to the address: address + c * size */ - result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target)); + result = write_abstract_arg(target, 1, args.address + c * args.size, riscv_xlen(target)); if (result != ERROR_OK) { - LOG_ERROR("Failed to write arg1 during write_memory_abstract()."); - return result; + LOG_TARGET_ERROR(target, "Failed to write arg1."); + return mem_access_result(MEM_ACCESS_FAILED_DM_ACCESS_FAILED); } } /* Execute the command */ - result = execute_abstract_command(target, command); - - if (info->has_aampostincrement == YNM_MAYBE) { - if (result == ERROR_OK) { - /* Safety: double-check that the address was really auto-incremented */ - riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target)); - if (new_address == address + size) { - LOG_DEBUG("aampostincrement is supported on this target."); - info->has_aampostincrement = YNM_YES; - } else { - LOG_WARNING("Buggy aampostincrement! Address not incremented correctly."); - info->has_aampostincrement = YNM_NO; - } - } else { - /* Try the same access but with postincrement disabled. */ - command = access_memory_command(target, false, width, false, true); - result = execute_abstract_command(target, command); - if (result == ERROR_OK) { - LOG_DEBUG("aampostincrement is not supported on this target."); - info->has_aampostincrement = YNM_NO; - } - } + uint32_t cmderr; + result = riscv013_execute_abstract_command(target, command, &cmderr); + if (use_aampostincrement && result != ERROR_OK && + cmderr == CMDERR_NOT_SUPPORTED) { + LOG_TARGET_DEBUG(target, "Trying the same abstract memory " + "write command, but without aampostincrement"); + use_aampostincrement = false; + command = access_memory_command(target, /* virtual = */ false, + width, /* postincrement = */ false, /* is_write = */ true); + result = riscv013_execute_abstract_command(target, command, &cmderr); } + /* TODO: + * (1) Only the 1st access can result in a 'skip' + * (2) Analyze cmderr value */ if (result != ERROR_OK) - return result; + return mem_access_result(MEM_ACCESS_SKIPPED_ABSTRACT_ACCESS_CMDERR); - if (info->has_aampostincrement == YNM_YES) + if (use_aampostincrement) updateaddr = false; - p += size; + p += args.size; } + return mem_access_result(MEM_ACCESS_OK); +} + +/** + * This function is used to start the memory-reading pipeline. + * The pipeline looks like this: + * memory -> s1 -> dm_data[0:1] -> debugger + * Prior to calling it, the program buffer should contain the appropriate + * program. + * This function sets DM_ABSTRACTAUTO_AUTOEXECDATA to trigger second stage of the + * pipeline (s1 -> dm_data[0:1]) whenever dm_data is read. + */ +static int read_memory_progbuf_inner_startup(struct target *target, + target_addr_t address, uint32_t increment, uint32_t index) +{ + /* s0 holds the next address to read from. + * s1 holds the next data value read. + * a0 is a counter in case increment is 0. + */ + if (register_write_direct(target, GDB_REGNO_S0, address + index * increment) + != ERROR_OK) + return ERROR_FAIL; + + if (/*is_repeated_read*/ increment == 0 && + register_write_direct(target, GDB_REGNO_A0, index) != ERROR_OK) + return ERROR_FAIL; + + /* AC_ACCESS_REGISTER_POSTEXEC is used to trigger first stage of the + * pipeline (memory -> s1) whenever this command is executed. + */ + const uint32_t startup_command = riscv013_access_register_command(target, + GDB_REGNO_S1, riscv_xlen(target), + AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC); + uint32_t cmderr; + if (riscv013_execute_abstract_command(target, startup_command, &cmderr) != ERROR_OK) + return ERROR_FAIL; + /* TODO: we need to modify error handling here. */ + /* NOTE: in case of timeout cmderr is set to CMDERR_NONE */ + + /* First read has just triggered. Result is in s1. + * dm_data registers contain the previous value of s1 (garbage). + */ + if (dm_write(target, DM_ABSTRACTAUTO, + set_field(0, DM_ABSTRACTAUTO_AUTOEXECDATA, 1)) != ERROR_OK) + return ERROR_FAIL; + + /* Read garbage from dm_data0, which triggers another execution of the + * program. Now dm_data contains the first good result (from s1), + * and s1 the next memory value. + */ + if (dm_read_exec(target, NULL, DM_DATA0) != ERROR_OK) + goto clear_abstractauto_and_fail; + + uint32_t abstractcs; + if (wait_for_idle(target, &abstractcs) != ERROR_OK) + goto clear_abstractauto_and_fail; + + cmderr = get_field32(abstractcs, DM_ABSTRACTCS_CMDERR); + switch (cmderr) { + case CMDERR_NONE: + return ERROR_OK; + case CMDERR_BUSY: + LOG_TARGET_ERROR(target, "Unexpected busy error. This is probably a hardware bug."); + /* fall through */ + default: + LOG_TARGET_DEBUG(target, "error when reading memory, cmderr=0x%" PRIx32, cmderr); + riscv013_clear_abstract_error(target); + goto clear_abstractauto_and_fail; + } +clear_abstractauto_and_fail: + dm_write(target, DM_ABSTRACTAUTO, 0); + return ERROR_FAIL; +} + +/** + * This function attempts to restore the pipeline after a busy on abstract + * access. + * Target's state is as follows: + * s0 contains address + index_on_target * increment + * s1 contains mem[address + (index_on_target - 1) * increment] + * dm_data[0:1] contains mem[address + (index_on_target - 2) * increment] + */ +static int read_memory_progbuf_inner_on_ac_busy(struct target *target, + uint32_t start_index, uint32_t *elements_read, + const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_read(args)); + + int res = riscv013_clear_abstract_error(target); + if (res != ERROR_OK) + return res; + res = increase_ac_busy_delay(target); + if (res != ERROR_OK) + return res; + + if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK) + return ERROR_FAIL; + + /* See how far we got by reading s0/a0 */ + uint32_t index_on_target; + + if (/*is_repeated_read*/ args.increment == 0) { + /* s0 is constant, a0 is incremented by one each execution */ + riscv_reg_t counter; + + if (register_read_direct(target, &counter, GDB_REGNO_A0) != ERROR_OK) + return ERROR_FAIL; + index_on_target = counter; + } else { + target_addr_t address_on_target; + + if (register_read_direct(target, &address_on_target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + index_on_target = (address_on_target - args.address) / + args.increment; + } + + /* According to the spec, if an abstract command fails, one can't make any + * assumptions about dm_data registers, so all the values in the pipeline + * are clobbered now and need to be reread. + */ + const uint32_t min_index_on_target = start_index + 2; + if (index_on_target < min_index_on_target) { + LOG_TARGET_ERROR(target, "Arithmetic does not work correctly on the target"); + return ERROR_FAIL; + } else if (index_on_target == min_index_on_target) { + LOG_TARGET_DEBUG(target, "No forward progress"); + } + const uint32_t next_index = (index_on_target - 2); + *elements_read = next_index - start_index; + LOG_TARGET_WARNING(target, "Re-reading memory from addresses 0x%" + TARGET_PRIxADDR " and 0x%" TARGET_PRIxADDR ".", + args.address + args.increment * next_index, + args.address + args.increment * (next_index + 1)); + return read_memory_progbuf_inner_startup(target, args.address, + args.increment, next_index); +} + +/** + * This function attempts to restore the pipeline after a dmi busy. + */ +static int read_memory_progbuf_inner_on_dmi_busy(struct target *target, + uint32_t start_index, uint32_t next_start_index, + const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_read(args)); + + LOG_TARGET_DEBUG(target, "DMI_STATUS_BUSY encountered in batch. Memory read [%" + PRIu32 ", %" PRIu32 ")", start_index, next_start_index); + if (start_index == next_start_index) + LOG_TARGET_DEBUG(target, "No forward progress"); + + if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK) + return ERROR_FAIL; + return read_memory_progbuf_inner_startup(target, args.address, + args.increment, next_start_index); +} + +/** + * This function extracts the data from the batch. + */ +static int read_memory_progbuf_inner_extract_batch_data(struct target *target, + const struct riscv_batch *batch, + uint32_t start_index, uint32_t elements_to_read, uint32_t *elements_read, + const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_read(args)); + + const bool two_reads_per_element = args.size > 4; + const uint32_t reads_per_element = (two_reads_per_element ? 2 : 1); + assert(!two_reads_per_element || riscv_xlen(target) == 64); + assert(elements_to_read <= UINT32_MAX / reads_per_element); + const uint32_t nreads = elements_to_read * reads_per_element; + for (uint32_t curr_idx = start_index, read = 0; read < nreads; ++read) { + switch (riscv_batch_get_dmi_read_op(batch, read)) { + case DMI_STATUS_BUSY: + *elements_read = curr_idx - start_index; + return read_memory_progbuf_inner_on_dmi_busy(target, start_index, curr_idx + , args); + case DMI_STATUS_FAILED: + LOG_TARGET_DEBUG(target, + "Batch memory read encountered DMI_STATUS_FAILED on read %" + PRIu32, read); + return ERROR_FAIL; + case DMI_STATUS_SUCCESS: + break; + default: + assert(0); + } + const uint32_t value = riscv_batch_get_dmi_read_data(batch, read); + uint8_t * const curr_buff = args.read_buffer + + curr_idx * args.size; + const target_addr_t curr_addr = args.address + + curr_idx * args.increment; + const uint32_t size = args.size; + + assert(size <= 8); + const bool is_odd_read = read % 2; + + if (two_reads_per_element && !is_odd_read) { + buf_set_u32(curr_buff + 4, 0, (size * 8) - 32, value); + continue; + } + const bool is_second_read = two_reads_per_element; + + buf_set_u32(curr_buff, 0, is_second_read ? 32 : (size * 8), value); + log_memory_access64(curr_addr, buf_get_u64(curr_buff, 0, size * 8), + size, /*is_read*/ true); + ++curr_idx; + } + *elements_read = elements_to_read; + return ERROR_OK; +} + +/** + * This function reads a batch of elements from memory. + * Prior to calling this function the folowing conditions should be met: + * - Appropriate program loaded to program buffer. + * - DM_ABSTRACTAUTO_AUTOEXECDATA is set. + */ +static int read_memory_progbuf_inner_run_and_process_batch(struct target *target, + struct riscv_batch *batch, const struct riscv_mem_access_args args, + uint32_t start_index, uint32_t elements_to_read, uint32_t *elements_read) +{ + assert(riscv_mem_access_is_read(args)); + + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + + /* Abstract commands are executed while running the batch. */ + dm->abstract_cmd_maybe_busy = true; + if (batch_run(target, batch) != ERROR_OK) + return ERROR_FAIL; + + uint32_t abstractcs; + if (wait_for_idle(target, &abstractcs) != ERROR_OK) + return ERROR_FAIL; + + uint32_t elements_to_extract_from_batch; + + uint32_t cmderr = get_field32(abstractcs, DM_ABSTRACTCS_CMDERR); + switch (cmderr) { + case CMDERR_NONE: + LOG_TARGET_DEBUG(target, "successful (partial?) memory read [%" + PRIu32 ", %" PRIu32 ")", start_index, start_index + elements_to_read); + elements_to_extract_from_batch = elements_to_read; + break; + case CMDERR_BUSY: + LOG_TARGET_DEBUG(target, "memory read resulted in busy response"); + if (read_memory_progbuf_inner_on_ac_busy(target, start_index, + &elements_to_extract_from_batch, args) + != ERROR_OK) + return ERROR_FAIL; + break; + default: + LOG_TARGET_DEBUG(target, "error when reading memory, cmderr=0x%" PRIx32, cmderr); + riscv013_clear_abstract_error(target); + return ERROR_FAIL; + } + + if (read_memory_progbuf_inner_extract_batch_data(target, batch, start_index, + elements_to_extract_from_batch, elements_read, args) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static uint32_t read_memory_progbuf_inner_fill_batch(struct riscv_batch *batch, + uint32_t count, uint32_t size) +{ + assert(size <= 8); + const uint32_t two_regs_used[] = {DM_DATA1, DM_DATA0}; + const uint32_t one_reg_used[] = {DM_DATA0}; + const uint32_t reads_per_element = size > 4 ? 2 : 1; + const uint32_t * const used_regs = size > 4 ? two_regs_used : one_reg_used; + const uint32_t batch_capacity = riscv_batch_available_scans(batch) / reads_per_element; + const uint32_t end = MIN(batch_capacity, count); + + for (uint32_t j = 0; j < end; ++j) { + /* TODO: reuse "abstract_data_read_fill_batch()" here. + * TODO: Only the read of "DM_DATA0" starts an abstract + * command, so the other read can use "RISCV_DELAY_BASE" + */ + for (uint32_t i = 0; i < reads_per_element; ++i) + riscv_batch_add_dm_read(batch, used_regs[i], + RISCV_DELAY_ABSTRACT_COMMAND); + } + return end; +} + +static int read_memory_progbuf_inner_try_to_read(struct target *target, + const struct riscv_mem_access_args args, uint32_t *elements_read, + uint32_t index, uint32_t loop_count) +{ + assert(riscv_mem_access_is_read(args)); + + struct riscv_batch *batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE); + if (!batch) + return ERROR_FAIL; + + const uint32_t elements_to_read = read_memory_progbuf_inner_fill_batch(batch, + loop_count - index, args.size); + + int result = read_memory_progbuf_inner_run_and_process_batch(target, batch, + args, index, elements_to_read, elements_read); + riscv_batch_free(batch); return result; } /** - * Read the requested memory, taking care to execute every read exactly once, - * even if cmderr=busy is encountered. + * read_memory_progbuf_inner_startup() must be called before calling this function + * with the address argument equal to curr_target_address. */ -static int read_memory_progbuf_inner(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +static int read_memory_progbuf_inner_ensure_forward_progress(struct target *target, + const struct riscv_mem_access_args args, uint32_t start_index) { - RISCV013_INFO(info); + assert(riscv_mem_access_is_read(args)); - int result = ERROR_OK; - - /* Write address to S0. */ - result = register_write_direct(target, GDB_REGNO_S0, address); - if (result != ERROR_OK) - return result; - - if (increment == 0 && - register_write_direct(target, GDB_REGNO_S2, 0) != ERROR_OK) + LOG_TARGET_DEBUG(target, + "Executing one loop iteration to ensure forward progress (index=%" + PRIu32 ")", start_index); + const target_addr_t curr_target_address = args.address + + start_index * args.increment; + uint8_t * const curr_buffer_address = args.read_buffer + + start_index * args.size; + const struct riscv_mem_access_args curr_access = { + .read_buffer = curr_buffer_address, + .address = curr_target_address, + .size = args.size, + .increment = args.increment, + }; + uint32_t elements_read; + if (read_memory_progbuf_inner_try_to_read(target, curr_access, &elements_read, + /*index*/ 0, /*loop_count*/ 1) != ERROR_OK) return ERROR_FAIL; - uint32_t command = access_register_command(target, GDB_REGNO_S1, - riscv_xlen(target), - AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC); - if (execute_abstract_command(target, command) != ERROR_OK) - return ERROR_FAIL; - - /* First read has just triggered. Result is in s1. */ - if (count == 1) { - uint64_t value; - if (register_read_direct(target, &value, GDB_REGNO_S1) != ERROR_OK) - return ERROR_FAIL; - buf_set_u64(buffer, 0, 8 * size, value); - log_memory_access(address, value, size, true); - return ERROR_OK; - } - - if (dmi_write(target, DM_ABSTRACTAUTO, - 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET) != ERROR_OK) - goto error; - /* Read garbage from dmi_data0, which triggers another execution of the - * program. Now dmi_data0 contains the first good result, and s1 the next - * memory value. */ - if (dmi_read_exec(target, NULL, DM_DATA0) != ERROR_OK) - goto error; - - /* read_addr is the next address that the hart will read from, which is the - * value in s0. */ - unsigned int index = 2; - while (index < count) { - riscv_addr_t read_addr = address + index * increment; - LOG_DEBUG("i=%d, count=%d, read_addr=0x%" PRIx64, index, count, read_addr); - /* The pipeline looks like this: - * memory -> s1 -> dm_data0 -> debugger - * Right now: - * s0 contains read_addr - * s1 contains mem[read_addr-size] - * dm_data0 contains[read_addr-size*2] + if (elements_read != 1) { + assert(elements_read == 0); + LOG_TARGET_DEBUG(target, "Can not ensure forward progress"); + /* FIXME: Here it would be better to retry the read and fail only if the + * delay is greater then some threshold. */ - - struct riscv_batch *batch = riscv_batch_alloc(target, 32, - info->dmi_busy_delay + info->ac_busy_delay); - if (!batch) - return ERROR_FAIL; - - unsigned int reads = 0; - for (unsigned int j = index; j < count; j++) { - if (size > 4) - riscv_batch_add_dmi_read(batch, DM_DATA1); - riscv_batch_add_dmi_read(batch, DM_DATA0); - - reads++; - if (riscv_batch_full(batch)) - break; - } - - batch_run(target, batch); - - /* Wait for the target to finish performing the last abstract command, - * and update our copy of cmderr. If we see that DMI is busy here, - * dmi_busy_delay will be incremented. */ - uint32_t abstractcs; - if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) - return ERROR_FAIL; - while (get_field(abstractcs, DM_ABSTRACTCS_BUSY)) - if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) - return ERROR_FAIL; - info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR); - - unsigned int next_index; - unsigned int ignore_last = 0; - switch (info->cmderr) { - case CMDERR_NONE: - LOG_DEBUG("successful (partial?) memory read"); - next_index = index + reads; - break; - case CMDERR_BUSY: - LOG_DEBUG("memory read resulted in busy response"); - - increase_ac_busy_delay(target); - riscv013_clear_abstract_error(target); - - dmi_write(target, DM_ABSTRACTAUTO, 0); - - uint32_t dmi_data0, dmi_data1 = 0; - /* This is definitely a good version of the value that we - * attempted to read when we discovered that the target was - * busy. */ - if (dmi_read(target, &dmi_data0, DM_DATA0) != ERROR_OK) { - riscv_batch_free(batch); - goto error; - } - if (size > 4 && dmi_read(target, &dmi_data1, DM_DATA1) != ERROR_OK) { - riscv_batch_free(batch); - goto error; - } - - /* See how far we got, clobbering dmi_data0. */ - if (increment == 0) { - uint64_t counter; - result = register_read_direct(target, &counter, GDB_REGNO_S2); - next_index = counter; - } else { - uint64_t next_read_addr; - result = register_read_direct(target, &next_read_addr, - GDB_REGNO_S0); - next_index = (next_read_addr - address) / increment; - } - if (result != ERROR_OK) { - riscv_batch_free(batch); - goto error; - } - - uint64_t value64 = (((uint64_t)dmi_data1) << 32) | dmi_data0; - buf_set_u64(buffer + (next_index - 2) * size, 0, 8 * size, value64); - log_memory_access(address + (next_index - 2) * size, value64, size, true); - - /* Restore the command, and execute it. - * Now DM_DATA0 contains the next value just as it would if no - * error had occurred. */ - dmi_write_exec(target, DM_COMMAND, command, true); - next_index++; - - dmi_write(target, DM_ABSTRACTAUTO, - 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); - - ignore_last = 1; - - break; - default: - LOG_DEBUG("error when reading memory, abstractcs=0x%08lx", (long)abstractcs); - riscv013_clear_abstract_error(target); - riscv_batch_free(batch); - result = ERROR_FAIL; - goto error; - } - - /* Now read whatever we got out of the batch. */ - dmi_status_t status = DMI_STATUS_SUCCESS; - unsigned int read = 0; - assert(index >= 2); - for (unsigned int j = index - 2; j < index + reads; j++) { - assert(j < count); - LOG_DEBUG("index=%d, reads=%d, next_index=%d, ignore_last=%d, j=%d", - index, reads, next_index, ignore_last, j); - if (j + 3 + ignore_last > next_index) - break; - - status = riscv_batch_get_dmi_read_op(batch, read); - uint64_t value = riscv_batch_get_dmi_read_data(batch, read); - read++; - if (status != DMI_STATUS_SUCCESS) { - /* If we're here because of busy count, dmi_busy_delay will - * already have been increased and busy state will have been - * cleared in dmi_read(). */ - /* In at least some implementations, we issue a read, and then - * can get busy back when we try to scan out the read result, - * and the actual read value is lost forever. Since this is - * rare in any case, we return error here and rely on our - * caller to reread the entire block. */ - LOG_WARNING("Batch memory read encountered DMI error %d. " - "Falling back on slower reads.", status); - riscv_batch_free(batch); - result = ERROR_FAIL; - goto error; - } - if (size > 4) { - status = riscv_batch_get_dmi_read_op(batch, read); - if (status != DMI_STATUS_SUCCESS) { - LOG_WARNING("Batch memory read encountered DMI error %d. " - "Falling back on slower reads.", status); - riscv_batch_free(batch); - result = ERROR_FAIL; - goto error; - } - value <<= 32; - value |= riscv_batch_get_dmi_read_data(batch, read); - read++; - } - riscv_addr_t offset = j * size; - buf_set_u64(buffer + offset, 0, 8 * size, value); - log_memory_access(address + j * increment, value, size, true); - } - - index = next_index; - - riscv_batch_free(batch); + return ERROR_FAIL; } - - dmi_write(target, DM_ABSTRACTAUTO, 0); - - if (count > 1) { - /* Read the penultimate word. */ - uint32_t dmi_data0, dmi_data1 = 0; - if (dmi_read(target, &dmi_data0, DM_DATA0) != ERROR_OK) - return ERROR_FAIL; - if (size > 4 && dmi_read(target, &dmi_data1, DM_DATA1) != ERROR_OK) - return ERROR_FAIL; - uint64_t value64 = (((uint64_t)dmi_data1) << 32) | dmi_data0; - buf_set_u64(buffer + size * (count - 2), 0, 8 * size, value64); - log_memory_access(address + size * (count - 2), value64, size, true); - } - - /* Read the last word. */ - uint64_t value; - result = register_read_direct(target, &value, GDB_REGNO_S1); - if (result != ERROR_OK) - goto error; - buf_set_u64(buffer + size * (count-1), 0, 8 * size, value); - log_memory_access(address + size * (count-1), value, size, true); - return ERROR_OK; +} -error: - dmi_write(target, DM_ABSTRACTAUTO, 0); +static void set_buffer_and_log_read(const struct riscv_mem_access_args args, + uint32_t index, uint64_t value) +{ + assert(riscv_mem_access_is_read(args)); + uint8_t * const buffer = args.read_buffer; + const uint32_t size = args.size; + const uint32_t increment = args.increment; + const target_addr_t address = args.address; + + assert(size <= 8); + buf_set_u64(buffer + index * size, 0, 8 * size, value); + log_memory_access64(address + index * increment, value, size, + /*is_read*/ true); +} + +static int read_word_from_dm_data_regs(struct target *target, + const struct riscv_mem_access_args args, uint32_t index) +{ + assert(args.size <= 8); + uint64_t value; + int result = read_abstract_arg(target, &value, /*index*/ 0, + args.size > 4 ? 64 : 32); + if (result == ERROR_OK) + set_buffer_and_log_read(args, index, value); return result; } -/* Only need to save/restore one GPR to read a single word, and the progbuf - * program doesn't need to increment. */ -static int read_memory_progbuf_one(struct target *target, target_addr_t address, - uint32_t size, uint8_t *buffer) +static struct mem_access_result read_word_from_s1(struct target *target, + const struct riscv_mem_access_args args, uint32_t index) { - uint64_t mstatus = 0; - uint64_t mstatus_old = 0; - if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) - return ERROR_FAIL; - - uint64_t s0; - int result = ERROR_FAIL; - - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) - goto restore_mstatus; - - /* Write the program (load, increment) */ - struct riscv_program program; - riscv_program_init(&program, target); - if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) - riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); - switch (size) { - case 1: - riscv_program_lbr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); - break; - case 2: - riscv_program_lhr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); - break; - case 4: - riscv_program_lwr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); - break; - case 8: - riscv_program_ldr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0); - break; - default: - LOG_ERROR("Unsupported size: %d", size); - goto restore_mstatus; - } - if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) - riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); - - if (riscv_program_ebreak(&program) != ERROR_OK) - goto restore_mstatus; - if (riscv_program_write(&program) != ERROR_OK) - goto restore_mstatus; - - /* Write address to S0, and execute buffer. */ - if (write_abstract_arg(target, 0, address, riscv_xlen(target)) != ERROR_OK) - goto restore_mstatus; - uint32_t command = access_register_command(target, GDB_REGNO_S0, - riscv_xlen(target), AC_ACCESS_REGISTER_WRITE | - AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC); - if (execute_abstract_command(target, command) != ERROR_OK) - goto restore_s0; + assert(riscv_mem_access_is_read(args)); uint64_t value; - if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK) - goto restore_s0; - buf_set_u64(buffer, 0, 8 * size, value); - log_memory_access(address, value, size, true); - result = ERROR_OK; -restore_s0: - if (riscv_set_register(target, GDB_REGNO_S0, s0) != ERROR_OK) - result = ERROR_FAIL; + if (register_read_direct(target, &value, GDB_REGNO_S1) != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED_REG_READ_FAILED); + set_buffer_and_log_read(args, index, value); + return mem_access_result(MEM_ACCESS_OK); +} -restore_mstatus: - if (mstatus != mstatus_old) - if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) - result = ERROR_FAIL; +static int read_memory_progbuf_inner_fill_progbuf(struct target *target, + uint32_t increment, uint32_t size) +{ + const bool is_repeated_read = increment == 0; - return result; + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + if (is_repeated_read && riscv013_reg_save(target, GDB_REGNO_A0) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + + riscv_program_init(&program, target); + if (riscv_program_load(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0, size) != ERROR_OK) + return ERROR_FAIL; + if (is_repeated_read) { + if (riscv_program_addi(&program, GDB_REGNO_A0, GDB_REGNO_A0, 1) + != ERROR_OK) + return ERROR_FAIL; + } else { + if (riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, + increment) + != ERROR_OK) + return ERROR_FAIL; + } + if (riscv_program_ebreak(&program) != ERROR_OK) + return ERROR_FAIL; + if (riscv_program_write(&program) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +/** + * Read the requested memory, taking care to minimize the number of reads and + * re-read the data only if `abstract command busy` or `DMI busy` + * is encountered in the process. + */ +static struct mem_access_result +read_memory_progbuf_inner(struct target *target, const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_read(args)); + assert(args.count > 1 && "If count == 1, read_memory_progbuf_inner_one must be called"); + + if (read_memory_progbuf_inner_fill_progbuf(target, + args.increment, args.size) != ERROR_OK) + return mem_access_result(MEM_ACCESS_SKIPPED_PROGBUF_FILL_FAILED); + + if (read_memory_progbuf_inner_startup(target, args.address, + args.increment, /*index*/ 0) != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED_PROGBUF_STARTUP_FAILED); + /* The program in program buffer is executed twice during + * read_memory_progbuf_inner_startup(). + * Here: + * dm_data[0:1] == M[address] + * s1 == M[address + increment] + * s0 == address + increment * 2 + * `count - 2` program executions are performed in this loop. + * No need to execute the program any more, since S1 will already contain + * M[address + increment * (count - 1)] and we can read it directly. + */ + const uint32_t loop_count = args.count - 2; + + for (uint32_t index = 0; index < loop_count;) { + uint32_t elements_read; + if (read_memory_progbuf_inner_try_to_read(target, args, &elements_read, + index, loop_count) != ERROR_OK) { + dm_write(target, DM_ABSTRACTAUTO, 0); + return mem_access_result(MEM_ACCESS_FAILED_PROGBUF_INNER_FAILED); + } + if (elements_read == 0) { + if (read_memory_progbuf_inner_ensure_forward_progress(target, args, + index) != ERROR_OK) { + dm_write(target, DM_ABSTRACTAUTO, 0); + return mem_access_result(MEM_ACCESS_FAILED_NO_FORWARD_PROGRESS); + } + elements_read = 1; + } + index += elements_read; + assert(index <= loop_count); + } + if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED_DM_ACCESS_FAILED); + + /* Read the penultimate word. */ + if (read_word_from_dm_data_regs(target, + args, args.count - 2) != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED_DM_ACCESS_FAILED); + /* Read the last word. */ + return read_word_from_s1(target, args, args.count - 1); +} + +/** + * Only need to save/restore one GPR to read a single word, and the progbuf + * program doesn't need to increment. + */ +static struct mem_access_result +read_memory_progbuf_inner_one(struct target *target, const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_read(args)); + + if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK) + return mem_access_result(MEM_ACCESS_SKIPPED_REG_SAVE_FAILED); + + struct riscv_program program; + + riscv_program_init(&program, target); + if (riscv_program_load(&program, GDB_REGNO_S1, GDB_REGNO_S1, + /* offset = */ 0, args.size) != ERROR_OK + || riscv_program_ebreak(&program) != ERROR_OK) + return mem_access_result(MEM_ACCESS_SKIPPED_PROGBUF_FILL_FAILED); + + if (riscv_program_write(&program) != ERROR_OK) + return mem_access_result(MEM_ACCESS_SKIPPED_PROGRAM_WRITE_FAILED); + + /* Write address to S1, and execute buffer. */ + if (write_abstract_arg(target, /* index = */ 0, + args.address, riscv_xlen(target)) != ERROR_OK) + return mem_access_result(MEM_ACCESS_SKIPPED_WRITE_ABSTRACT_ARG_FAILED); + uint32_t command = riscv013_access_register_command(target, GDB_REGNO_S1, + riscv_xlen(target), AC_ACCESS_REGISTER_WRITE | + AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC); + uint32_t cmderr; + if (riscv013_execute_abstract_command(target, command, &cmderr) != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED_EXECUTE_ABSTRACT_FAILED); + + return read_word_from_s1(target, args, 0); } /** * Read the requested memory, silently handling memory access errors. */ -static int read_memory_progbuf(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +static struct mem_access_result +read_memory_progbuf(struct target *target, const struct riscv_mem_access_args args) { - if (riscv_xlen(target) < size * 8) { - LOG_ERROR("XLEN (%d) is too short for %d-bit memory read.", - riscv_xlen(target), size * 8); - return ERROR_FAIL; - } + assert(riscv_mem_access_is_read(args)); - int result = ERROR_OK; + select_dmi(target->tap); + memset(args.read_buffer, 0, args.count * args.size); - LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, - size, address); + if (execute_autofence(target) != ERROR_OK) + return mem_access_result(MEM_ACCESS_SKIPPED_FENCE_EXEC_FAILED); - select_dmi(target); + return (args.count == 1) ? + read_memory_progbuf_inner_one(target, args) : + read_memory_progbuf_inner(target, args); +} - memset(buffer, 0, count*size); +static struct mem_access_result +write_memory_progbuf(struct target *target, const struct riscv_mem_access_args args); - if (execute_fence(target) != ERROR_OK) - return ERROR_FAIL; +static struct mem_access_result +access_memory_progbuf(struct target *target, const struct riscv_mem_access_args args) +{ + struct mem_access_result skip_reason = mem_should_skip_progbuf(target, args); + if (!is_mem_access_ok(skip_reason)) + return skip_reason; - if (count == 1) - return read_memory_progbuf_one(target, address, size, buffer); + const bool is_read = riscv_mem_access_is_read(args); + const char *const access_type = is_read ? "reading" : "writing"; + LOG_TARGET_DEBUG(target, "%s %" PRIu32 " words of %" PRIu32 + " bytes at 0x%" TARGET_PRIxADDR, access_type, args.count, + args.size, args.address); - uint64_t mstatus = 0; - uint64_t mstatus_old = 0; - if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) - return ERROR_FAIL; + if (dm013_select_target(target) != ERROR_OK) + return mem_access_result(MEM_ACCESS_SKIPPED_TARGET_SELECT_FAILED); - /* s0 holds the next address to read from - * s1 holds the next data value read - * s2 is a counter in case increment is 0 - */ - uint64_t s0, s1, s2; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) - return ERROR_FAIL; - if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK) - return ERROR_FAIL; - if (increment == 0 && register_read(target, &s2, GDB_REGNO_S2) != ERROR_OK) - return ERROR_FAIL; + riscv_reg_t mstatus = 0; + riscv_reg_t mstatus_old = 0; + riscv_reg_t dcsr = 0; + riscv_reg_t dcsr_old = 0; + if (modify_privilege_for_virt2phys_mode(target, + &mstatus, &mstatus_old, &dcsr, &dcsr_old) != ERROR_OK) + return mem_access_result(MEM_ACCESS_SKIPPED_PRIV_MOD_FAILED); - /* Write the program (load, increment) */ - struct riscv_program program; - riscv_program_init(&program, target); - if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) - riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); + struct mem_access_result result = is_read ? + read_memory_progbuf(target, args) : + write_memory_progbuf(target, args); - switch (size) { - case 1: - riscv_program_lbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - case 2: - riscv_program_lhr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - case 4: - riscv_program_lwr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - case 8: - riscv_program_ldr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - default: - LOG_ERROR("Unsupported size: %d", size); - return ERROR_FAIL; - } - - if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) - riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); - if (increment == 0) - riscv_program_addi(&program, GDB_REGNO_S2, GDB_REGNO_S2, 1); - else - riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, increment); - - if (riscv_program_ebreak(&program) != ERROR_OK) - return ERROR_FAIL; - if (riscv_program_write(&program) != ERROR_OK) - return ERROR_FAIL; - - result = read_memory_progbuf_inner(target, address, size, count, buffer, increment); - - if (result != ERROR_OK) { - /* The full read did not succeed, so we will try to read each word individually. */ - /* This will not be fast, but reading outside actual memory is a special case anyway. */ - /* It will make the toolchain happier, especially Eclipse Memory View as it reads ahead. */ - target_addr_t address_i = address; - uint32_t count_i = 1; - uint8_t *buffer_i = buffer; - - for (uint32_t i = 0; i < count; i++, address_i += increment, buffer_i += size) { - /* TODO: This is much slower than it needs to be because we end up - * writing the address to read for every word we read. */ - result = read_memory_progbuf_inner(target, address_i, size, count_i, buffer_i, increment); - - /* The read of a single word failed, so we will just return 0 for that instead */ - if (result != ERROR_OK) { - LOG_DEBUG("error reading single word of %d bytes from 0x%" TARGET_PRIxADDR, - size, address_i); - - buf_set_u64(buffer_i, 0, 8 * size, 0); - } - } - result = ERROR_OK; - } - - riscv_set_register(target, GDB_REGNO_S0, s0); - riscv_set_register(target, GDB_REGNO_S1, s1); - if (increment == 0) - riscv_set_register(target, GDB_REGNO_S2, s2); - - /* Restore MSTATUS */ - if (mstatus != mstatus_old) - if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) - return ERROR_FAIL; + if (restore_privilege_from_virt2phys_mode(target, + mstatus, mstatus_old, dcsr, dcsr_old) != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED_PRIV_MOD_FAILED); return result; } -static int read_memory(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) -{ - if (count == 0) - return ERROR_OK; +static int +write_memory_bus_v0(struct target *target, const struct riscv_mem_access_args args); +static int +write_memory_bus_v1(struct target *target, const struct riscv_mem_access_args args); - if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) { - LOG_ERROR("BUG: Unsupported size for memory read: %d", size); +static struct mem_access_result +access_memory_sysbus(struct target *target, const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_valid(args)); + + struct mem_access_result skip_reason = mem_should_skip_sysbus(target, args); + if (!is_mem_access_ok(skip_reason)) + return skip_reason; + + RISCV013_INFO(info); + int ret = ERROR_FAIL; + const bool is_read = riscv_mem_access_is_read(args); + const uint64_t sbver = get_field(info->sbcs, DM_SBCS_SBVERSION); + if (sbver == 0) { + ret = is_read ? read_memory_bus_v0(target, args) : + write_memory_bus_v0(target, args); + } else if (sbver == 1) { + ret = is_read ? read_memory_bus_v1(target, args) : + write_memory_bus_v1(target, args); + } else { + LOG_TARGET_ERROR(target, "Unknown system bus version: %" PRIu64, sbver); + return mem_access_result(MEM_ACCESS_SKIPPED_UNKNOWN_SYSBUS_VERSION); + } + + return mem_access_result(ret == ERROR_OK ? + MEM_ACCESS_OK : MEM_ACCESS_SKIPPED_SYSBUS_ACCESS_FAILED); +} + +static struct mem_access_result +access_memory_abstract(struct target *target, const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_valid(args)); + + struct mem_access_result skip_reason = mem_should_skip_abstract(target, args); + if (!is_mem_access_ok(skip_reason)) + return skip_reason; + + const bool is_read = riscv_mem_access_is_read(args); + const char *const access_type = is_read ? "reading" : "writing"; + LOG_TARGET_DEBUG(target, "%s %d words of %d bytes at 0x%" + TARGET_PRIxADDR, access_type, args.count, + args.size, args.address); + + return is_read ? read_memory_abstract(target, args) : + write_memory_abstract(target, args); +} + +static int +riscv013_access_memory(struct target *target, const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_valid(args)); + + const bool is_read = riscv_mem_access_is_read(args); + const char *const access_type = is_read ? "read" : "write"; + if (!is_read && args.increment != args.size) { + LOG_TARGET_ERROR(target, "Write increment size has to be equal to element size"); + return ERROR_NOT_IMPLEMENTED; + } + + if (!IS_PWR_OF_2(args.size) || args.size < 1 || args.size > 16) { + LOG_TARGET_ERROR(target, "BUG: Unsupported size for " + "memory %s: %d", access_type, args.size); return ERROR_FAIL; } - int ret = ERROR_FAIL; + struct mem_access_result skip_reason[] = { + [RISCV_MEM_ACCESS_PROGBUF] = mem_access_result(MEM_ACCESS_DISABLED), + [RISCV_MEM_ACCESS_SYSBUS] = mem_access_result(MEM_ACCESS_DISABLED), + [RISCV_MEM_ACCESS_ABSTRACT] = mem_access_result(MEM_ACCESS_DISABLED), + }; + RISCV_INFO(r); - RISCV013_INFO(info); + for (unsigned int i = 0; i < r->num_enabled_mem_access_methods; ++i) { + enum riscv_mem_access_method method = r->mem_access_methods[i]; + switch (method) { + case RISCV_MEM_ACCESS_PROGBUF: + skip_reason[method] = access_memory_progbuf(target, args); + break; + case RISCV_MEM_ACCESS_SYSBUS: + skip_reason[method] = access_memory_sysbus(target, args); + break; + case RISCV_MEM_ACCESS_ABSTRACT: + skip_reason[method] = access_memory_abstract(target, args); + break; + default: + LOG_TARGET_ERROR(target, "Unknown memory access method: %d", method); + assert(false && "Unknown memory access method"); + goto failure; + } - char *progbuf_result = "disabled"; - char *sysbus_result = "disabled"; - char *abstract_result = "disabled"; + if (is_mem_access_failed(skip_reason[method])) + goto failure; - for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { - int method = r->mem_access_methods[i]; - - if (method == RISCV_MEM_ACCESS_PROGBUF) { - if (mem_should_skip_progbuf(target, address, size, true, &progbuf_result)) - continue; - - ret = read_memory_progbuf(target, address, size, count, buffer, increment); - - if (ret != ERROR_OK) - progbuf_result = "failed"; - } else if (method == RISCV_MEM_ACCESS_SYSBUS) { - if (mem_should_skip_sysbus(target, address, size, increment, true, &sysbus_result)) - continue; - - if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0) - ret = read_memory_bus_v0(target, address, size, count, buffer, increment); - else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1) - ret = read_memory_bus_v1(target, address, size, count, buffer, increment); - - if (ret != ERROR_OK) - sysbus_result = "failed"; - } else if (method == RISCV_MEM_ACCESS_ABSTRACT) { - if (mem_should_skip_abstract(target, address, size, increment, true, &abstract_result)) - continue; - - ret = read_memory_abstract(target, address, size, count, buffer, increment); - - if (ret != ERROR_OK) - abstract_result = "failed"; - } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED) - /* No further mem access method to try. */ - break; - - log_mem_access_result(target, ret == ERROR_OK, method, true); - - if (ret == ERROR_OK) - return ret; + const bool success = is_mem_access_ok(skip_reason[method]); + log_mem_access_result(target, success, method, is_read); + if (success) + return ERROR_OK; } - LOG_ERROR("Target %s: Failed to read memory (addr=0x%" PRIx64 ")", target_name(target), address); - LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); - return ret; +failure: + LOG_TARGET_ERROR(target, "Failed to %s memory (addr=0x%" PRIx64 ")\n" + " progbuf=%s, sysbus=%s, abstract=%s", access_type, args.address, + mem_access_result_to_str(skip_reason[RISCV_MEM_ACCESS_PROGBUF]), + mem_access_result_to_str(skip_reason[RISCV_MEM_ACCESS_SYSBUS]), + mem_access_result_to_str(skip_reason[RISCV_MEM_ACCESS_ABSTRACT])); + return ERROR_FAIL; } -static int write_memory_bus_v0(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) +static int write_memory_bus_v0(struct target *target, const struct riscv_mem_access_args args) { + assert(riscv_mem_access_is_write(args)); + /*1) write sbaddress: for singlewrite and autoincrement, we need to write the address once*/ - LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08" - TARGET_PRIxADDR, size, count, address); - dmi_write(target, DM_SBADDRESS0, address); + LOG_TARGET_DEBUG(target, "System Bus Access: size: %d\tcount:%d\tstart address: 0x%08" + TARGET_PRIxADDR, args.size, args.count, args.address); + dm_write(target, DM_SBADDRESS0, args.address); int64_t value = 0; int64_t access = 0; riscv_addr_t offset = 0; riscv_addr_t t_addr = 0; - const uint8_t *t_buffer = buffer + offset; + const uint8_t *t_buffer = args.write_buffer + offset; /* B.8 Writing Memory, single write check if we write in one go */ - if (count == 1) { /* count is in bytes here */ - value = buf_get_u64(t_buffer, 0, 8 * size); + if (args.count == 1) { /* count is in bytes here */ + value = buf_get_u64(t_buffer, 0, 8 * args.size); access = 0; - access = set_field(access, DM_SBCS_SBACCESS, size/2); - dmi_write(target, DM_SBCS, access); - LOG_DEBUG("\r\naccess: 0x%08" PRIx64, access); - LOG_DEBUG("\r\nwrite_memory:SAB: ONE OFF: value 0x%08" PRIx64, value); - dmi_write(target, DM_SBDATA0, value); + access = set_field(access, DM_SBCS_SBACCESS, args.size / 2); + dm_write(target, DM_SBCS, access); + LOG_TARGET_DEBUG(target, " access: 0x%08" PRIx64, access); + LOG_TARGET_DEBUG(target, " write_memory:SAB: ONE OFF: value 0x%08" PRIx64, value); + dm_write(target, DM_SBDATA0, value); return ERROR_OK; } /*B.8 Writing Memory, using autoincrement*/ access = 0; - access = set_field(access, DM_SBCS_SBACCESS, size/2); + access = set_field(access, DM_SBCS_SBACCESS, args.size / 2); access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 1); - LOG_DEBUG("\r\naccess: 0x%08" PRIx64, access); - dmi_write(target, DM_SBCS, access); + LOG_TARGET_DEBUG(target, " access: 0x%08" PRIx64, access); + dm_write(target, DM_SBCS, access); /*2)set the value according to the size required and write*/ - for (riscv_addr_t i = 0; i < count; ++i) { - offset = size*i; + for (riscv_addr_t i = 0; i < args.count; ++i) { + offset = args.size * i; /* for monitoring only */ - t_addr = address + offset; - t_buffer = buffer + offset; + t_addr = args.address + offset; + t_buffer = args.write_buffer + offset; - value = buf_get_u64(t_buffer, 0, 8 * size); - LOG_DEBUG("SAB:autoincrement: expected address: 0x%08x value: 0x%08x" + value = buf_get_u64(t_buffer, 0, 8 * args.size); + LOG_TARGET_DEBUG(target, "SAB:autoincrement: expected address: 0x%08x value: 0x%08x" PRIx64, (uint32_t)t_addr, (uint32_t)value); - dmi_write(target, DM_SBDATA0, value); + dm_write(target, DM_SBDATA0, value); } /*reset the autoincrement when finished (something weird is happening if this is not done at the end*/ access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 0); - dmi_write(target, DM_SBCS, access); + dm_write(target, DM_SBCS, access); return ERROR_OK; } -static int write_memory_bus_v1(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) +static int write_memory_bus_v1(struct target *target, const struct riscv_mem_access_args args) { + assert(riscv_mem_access_is_write(args)); + RISCV013_INFO(info); - uint32_t sbcs = sb_sbaccess(size); + uint32_t sbcs = sb_sbaccess(args.size); sbcs = set_field(sbcs, DM_SBCS_SBAUTOINCREMENT, 1); - dmi_write(target, DM_SBCS, sbcs); + dm_write(target, DM_SBCS, sbcs); - target_addr_t next_address = address; - target_addr_t end_address = address + count * size; + target_addr_t next_address = args.address; + target_addr_t end_address = args.address + args.count * args.size; - int result; + int result = sb_write_address(target, next_address, RISCV_DELAY_BASE); + if (result != ERROR_OK) + return result; - sb_write_address(target, next_address, true); while (next_address < end_address) { - LOG_DEBUG("transferring burst starting at address 0x%" TARGET_PRIxADDR, + LOG_TARGET_DEBUG(target, "Transferring burst starting at address 0x%" TARGET_PRIxADDR, next_address); - struct riscv_batch *batch = riscv_batch_alloc( - target, - 32, - info->dmi_busy_delay + info->bus_master_write_delay); + struct riscv_batch *batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE); if (!batch) return ERROR_FAIL; - for (uint32_t i = (next_address - address) / size; i < count; i++) { - const uint8_t *p = buffer + i * size; + for (uint32_t i = (next_address - args.address) / args.size; i < args.count; i++) { + const uint8_t *p = args.write_buffer + i * args.size; - if (riscv_batch_available_scans(batch) < (size + 3) / 4) + if (riscv_batch_available_scans(batch) < (args.size + 3) / 4) break; - if (size > 12) - riscv_batch_add_dmi_write(batch, DM_SBDATA3, - ((uint32_t) p[12]) | - (((uint32_t) p[13]) << 8) | - (((uint32_t) p[14]) << 16) | - (((uint32_t) p[15]) << 24)); - - if (size > 8) - riscv_batch_add_dmi_write(batch, DM_SBDATA2, - ((uint32_t) p[8]) | - (((uint32_t) p[9]) << 8) | - (((uint32_t) p[10]) << 16) | - (((uint32_t) p[11]) << 24)); - if (size > 4) - riscv_batch_add_dmi_write(batch, DM_SBDATA1, - ((uint32_t) p[4]) | - (((uint32_t) p[5]) << 8) | - (((uint32_t) p[6]) << 16) | - (((uint32_t) p[7]) << 24)); - uint32_t value = p[0]; - if (size > 2) { - value |= ((uint32_t) p[2]) << 16; - value |= ((uint32_t) p[3]) << 24; + uint32_t sbvalue[4] = { 0 }; + if (args.size > 12) { + sbvalue[3] = buf_get_u32(&p[12], + /* first = */ 0, /* bit_num = */ 32); + riscv_batch_add_dm_write(batch, DM_SBDATA3, sbvalue[3], false, + RISCV_DELAY_BASE); } - if (size > 1) - value |= ((uint32_t) p[1]) << 8; - riscv_batch_add_dmi_write(batch, DM_SBDATA0, value); - log_memory_access(address + i * size, value, size, false); - next_address += size; + if (args.size > 8) { + sbvalue[2] = buf_get_u32(&p[8], + /* first = */ 0, /* bit_num = */ 32); + riscv_batch_add_dm_write(batch, DM_SBDATA2, sbvalue[2], false, + RISCV_DELAY_BASE); + } + if (args.size > 4) { + sbvalue[1] = buf_get_u32(&p[4], + /* first = */ 0, /* bit_num = */ 32); + riscv_batch_add_dm_write(batch, DM_SBDATA1, sbvalue[1], false, + RISCV_DELAY_BASE); + } + + sbvalue[0] = p[0]; + if (args.size > 2) { + sbvalue[0] |= ((uint32_t)p[2]) << 16; + sbvalue[0] |= ((uint32_t)p[3]) << 24; + } + if (args.size > 1) + sbvalue[0] |= ((uint32_t)p[1]) << 8; + + riscv_batch_add_dm_write(batch, DM_SBDATA0, sbvalue[0], false, + RISCV_DELAY_SYSBUS_WRITE); + + log_memory_access(args.address + i * args.size, sbvalue, args.size, false); + + next_address += args.size; } /* Execute the batch of writes */ result = batch_run(target, batch); + if (result != ERROR_OK) { + riscv_batch_free(batch); + return result; + } + + bool dmi_busy_encountered = riscv_batch_was_batch_busy(batch); riscv_batch_free(batch); + if (dmi_busy_encountered) + LOG_TARGET_DEBUG(target, "DMI busy encountered during system bus write."); + + result = read_sbcs_nonbusy(target, &sbcs); if (result != ERROR_OK) return result; - /* Read sbcs value. - * At the same time, detect if DMI busy has occurred during the batch write. */ - bool dmi_busy_encountered; - if (dmi_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ, - DM_SBCS, 0, false, true) != ERROR_OK) - return ERROR_FAIL; - if (dmi_busy_encountered) - LOG_DEBUG("DMI busy encountered during system bus write."); - - /* Wait until sbbusy goes low */ - time_t start = time(NULL); - while (get_field(sbcs, DM_SBCS_SBBUSY)) { - if (time(NULL) - start > riscv_command_timeout_sec) { - LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " - "Increase the timeout with riscv set_command_timeout_sec.", - riscv_command_timeout_sec, sbcs); - return ERROR_FAIL; - } - if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK) - return ERROR_FAIL; - } - if (get_field(sbcs, DM_SBCS_SBBUSYERROR)) { /* We wrote while the target was busy. */ - LOG_DEBUG("Sbbusyerror encountered during system bus write."); + LOG_TARGET_DEBUG(target, "Sbbusyerror encountered during system bus write."); /* Clear the sticky error flag. */ - dmi_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR); - /* Slow down before trying again. */ - info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1; + dm_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR); + /* Slow down before trying again. + * FIXME: Possible overflow is ignored here. + */ + riscv_scan_increase_delay(&info->learned_delays, + RISCV_DELAY_SYSBUS_WRITE); } if (get_field(sbcs, DM_SBCS_SBBUSYERROR) || dmi_busy_encountered) { /* Recover from the case when the write commands were issued too fast. * Determine the address from which to resume writing. */ next_address = sb_read_address(target); - if (next_address < address) { + if (next_address < args.address) { /* This should never happen, probably buggy hardware. */ - LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR + LOG_TARGET_DEBUG(target, "unexpected sbaddress=0x%" TARGET_PRIxADDR " - buggy sbautoincrement in hw?", next_address); /* Fail the whole operation. */ return ERROR_FAIL; @@ -3767,16 +4784,16 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, * (unless sbautoincrement in the HW is buggy). */ target_addr_t sbaddress = sb_read_address(target); - LOG_DEBUG("System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")", + LOG_TARGET_DEBUG(target, "System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")", sberror, sbaddress); - if (sbaddress < address) { + if (sbaddress < args.address) { /* This should never happen, probably buggy hardware. * Make a note to the user not to trust the sbaddress value. */ - LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR + LOG_TARGET_DEBUG(target, "unexpected sbaddress=0x%" TARGET_PRIxADDR " - buggy sbautoincrement in hw?", next_address); } /* Clear the sticky error flag */ - dmi_write(target, DM_SBCS, DM_SBCS_SBERROR); + dm_write(target, DM_SBCS, DM_SBCS_SBERROR); /* Fail the whole operation */ return ERROR_FAIL; } @@ -3785,257 +4802,273 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, return ERROR_OK; } -static int write_memory_progbuf(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) +/** + * This function is used to start the memory-writing pipeline. + * As part of the process, the function writes the first item and waits for completion, + * so forward progress is ensured. + * The pipeline looks like this: + * debugger -> dm_data[0:1] -> s1 -> memory + * Prior to calling it, the program buffer should contain the appropriate + * program. + * This function sets DM_ABSTRACTAUTO_AUTOEXECDATA to trigger second stage of the + * pipeline (dm_data[0:1] -> s1) whenever dm_data is written. + */ +static int write_memory_progbuf_startup(struct target *target, target_addr_t *address_p, + const uint8_t *buffer, uint32_t size) { - RISCV013_INFO(info); - - if (riscv_xlen(target) < size * 8) { - LOG_ERROR("XLEN (%d) is too short for %d-bit memory write.", - riscv_xlen(target), size * 8); - return ERROR_FAIL; - } - - LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address); - - select_dmi(target); - - uint64_t mstatus = 0; - uint64_t mstatus_old = 0; - if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) + /* TODO: There is potential to gain some performance if the operations below are + * executed inside the first DMI batch (not separately). */ + if (register_write_direct(target, GDB_REGNO_S0, *address_p) != ERROR_OK) return ERROR_FAIL; - /* s0 holds the next address to write to - * s1 holds the next data value to write + /* Write the first item to data0 [, data1] */ + assert(size <= 8); + const uint64_t value = buf_get_u64(buffer, 0, 8 * size); + if (write_abstract_arg(target, /*index*/ 0, value, size > 4 ? 64 : 32) + != ERROR_OK) + return ERROR_FAIL; + + /* Write and execute command that moves the value from data0 [, data1] + * into S1 and executes program buffer. */ + uint32_t command = riscv013_access_register_command(target, + GDB_REGNO_S1, riscv_xlen(target), + AC_ACCESS_REGISTER_POSTEXEC | + AC_ACCESS_REGISTER_TRANSFER | + AC_ACCESS_REGISTER_WRITE); + + uint32_t cmderr; + if (riscv013_execute_abstract_command(target, command, &cmderr) != ERROR_OK) + return ERROR_FAIL; + + log_memory_access64(*address_p, value, size, /*is_read*/ false); + + /* The execution of the command succeeded, which means: + * - write of the first item to memory succeeded + * - address on the target (S0) was incremented */ + *address_p += size; - int result = ERROR_OK; - uint64_t s0, s1; - if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) - return ERROR_FAIL; - if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK) + /* TODO: Setting abstractauto.autoexecdata is not necessary for a write + * of one element. */ + return dm_write(target, DM_ABSTRACTAUTO, + 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); +} + +/** + * This function reverts the changes made by `write_memory_progbuf_startup()` + */ +static int write_memory_progbuf_teardown(struct target *target) +{ + return dm_write(target, DM_ABSTRACTAUTO, 0); +} + +/** + * This function attempts to restore the pipeline after a busy on abstract + * access or a DMI busy by reading the content of s0 -- the address of the + * failed write. + */ +static int write_memory_progbuf_handle_busy(struct target *target, + target_addr_t *address_p, uint32_t size, const uint8_t *buffer) +{ + int res = riscv013_clear_abstract_error(target); + if (res != ERROR_OK) + return res; + res = increase_ac_busy_delay(target); + if (res != ERROR_OK) + return res; + + if (write_memory_progbuf_teardown(target) != ERROR_OK) + return ERROR_FAIL; + + target_addr_t address_on_target; + if (register_read_direct(target, &address_on_target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + const uint8_t * const curr_buff = buffer + (address_on_target - *address_p); + LOG_TARGET_DEBUG(target, "Restarting from 0x%" TARGET_PRIxADDR, *address_p); + *address_p = address_on_target; + /* This restores the pipeline and ensures one item gets reliably written */ + return write_memory_progbuf_startup(target, address_p, curr_buff, size); +} + +/** + * This function fills the batch with DMI writes (but does not execute the batch). + * It returns the next address -- the address that will be the start of the next batch. + */ +static target_addr_t write_memory_progbuf_fill_batch(struct riscv_batch *batch, + target_addr_t start_address, target_addr_t end_address, uint32_t size, + const uint8_t *buffer) +{ + assert(size <= 8); + const unsigned int writes_per_element = size > 4 ? 2 : 1; + const size_t batch_capacity = riscv_batch_available_scans(batch) / writes_per_element; + /* This is safe even for the edge case when writing at the very top of + * the 64-bit address space (in which case end_address overflows to 0). + */ + const target_addr_t batch_end_address = start_address + + MIN((target_addr_t)batch_capacity * size, + end_address - start_address); + for (target_addr_t address = start_address; address != batch_end_address; + address += size, buffer += size) { + assert(size <= 8); + const uint64_t value = buf_get_u64(buffer, 0, 8 * size); + log_memory_access64(address, value, size, /*is_read*/ false); + if (writes_per_element == 2) + riscv_batch_add_dm_write(batch, DM_DATA1, + (uint32_t)(value >> 32), false, RISCV_DELAY_BASE); + riscv_batch_add_dm_write(batch, DM_DATA0, (uint32_t)value, false, + RISCV_DELAY_ABSTRACT_COMMAND); + } + return batch_end_address; +} + +/** + * This function runs the batch of writes and updates address_p with the + * address of the next write. + */ +static int write_memory_progbuf_run_batch(struct target *target, struct riscv_batch *batch, + target_addr_t *address_p, target_addr_t end_address, uint32_t size, + const uint8_t *buffer) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + + /* Abstract commands are executed while running the batch. */ + dm->abstract_cmd_maybe_busy = true; + if (batch_run(target, batch) != ERROR_OK) + return ERROR_FAIL; + + /* Note that if the scan resulted in a Busy DMI response, it + * is this call to wait_for_idle() that will cause the dmi_busy_delay + * to be incremented if necessary. */ + uint32_t abstractcs; + + if (wait_for_idle(target, &abstractcs) != ERROR_OK) + return ERROR_FAIL; + + uint32_t cmderr = get_field32(abstractcs, DM_ABSTRACTCS_CMDERR); + const bool dmi_busy_encountered = riscv_batch_was_batch_busy(batch); + if (cmderr == CMDERR_NONE && !dmi_busy_encountered) { + LOG_TARGET_DEBUG(target, "Successfully written memory block M[0x%" TARGET_PRIxADDR + ".. 0x%" TARGET_PRIxADDR ")", *address_p, end_address); + *address_p = end_address; + return ERROR_OK; + } else if (cmderr == CMDERR_BUSY || dmi_busy_encountered) { + if (cmderr == CMDERR_BUSY) + LOG_TARGET_DEBUG(target, "Encountered abstract command busy response while writing block M[0x%" + TARGET_PRIxADDR ".. 0x%" TARGET_PRIxADDR ")", *address_p, end_address); + if (dmi_busy_encountered) + LOG_TARGET_DEBUG(target, "Encountered DMI busy response while writing block M[0x%" + TARGET_PRIxADDR ".. 0x%" TARGET_PRIxADDR ")", *address_p, end_address); + /* TODO: If dmi busy is encountered, the address of the last + * successful write can be deduced by analysing the batch. + */ + return write_memory_progbuf_handle_busy(target, address_p, size, buffer); + } + LOG_TARGET_ERROR(target, "Error when writing memory, abstractcs=0x%" PRIx32, + abstractcs); + riscv013_clear_abstract_error(target); + return ERROR_FAIL; +} + +static int write_memory_progbuf_try_to_write(struct target *target, + target_addr_t *address_p, target_addr_t end_address, uint32_t size, + const uint8_t *buffer) +{ + struct riscv_batch * const batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE); + if (!batch) + return ERROR_FAIL; + + const target_addr_t batch_end_addr = write_memory_progbuf_fill_batch(batch, + *address_p, end_address, size, buffer); + + int result = write_memory_progbuf_run_batch(target, batch, address_p, + batch_end_addr, size, buffer); + riscv_batch_free(batch); + return result; +} + +static int write_memory_progbuf_fill_progbuf(struct target *target, uint32_t size) +{ + if (riscv013_reg_save(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + if (riscv013_reg_save(target, GDB_REGNO_S1) != ERROR_OK) return ERROR_FAIL; - /* Write the program (store, increment) */ struct riscv_program program; + riscv_program_init(&program, target); - if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) - riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); + if (riscv_program_store(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0, size) != ERROR_OK) + return ERROR_FAIL; - switch (size) { - case 1: - riscv_program_sbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - case 2: - riscv_program_shr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - case 4: - riscv_program_swr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - case 8: - riscv_program_sdr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0); - break; - default: - LOG_ERROR("write_memory_progbuf(): Unsupported size: %d", size); - result = ERROR_FAIL; - goto error; + if (riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, (int16_t)size) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_program_ebreak(&program) != ERROR_OK) + return ERROR_FAIL; + + return riscv_program_write(&program); +} + +static struct mem_access_result +write_memory_progbuf_inner(struct target *target, + const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_write(args)); + + if (write_memory_progbuf_fill_progbuf(target, args.size) != ERROR_OK) + return mem_access_result(MEM_ACCESS_SKIPPED_PROGBUF_FILL_FAILED); + + target_addr_t addr_on_target = args.address; + if (write_memory_progbuf_startup(target, &addr_on_target, + args.write_buffer, args.size) != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED_PROGBUF_STARTUP_FAILED); + + const target_addr_t end_addr = args.address + (target_addr_t)args.size * args.count; + + for (target_addr_t next_addr_on_target = addr_on_target; addr_on_target != end_addr; + addr_on_target = next_addr_on_target) { + const uint8_t * const curr_buff = args.write_buffer + (addr_on_target - args.address); + if (write_memory_progbuf_try_to_write(target, &next_addr_on_target, + end_addr, args.size, curr_buff) != ERROR_OK) { + write_memory_progbuf_teardown(target); + return mem_access_result(MEM_ACCESS_FAILED_PROGBUF_INNER_FAILED); + } + /* write_memory_progbuf_try_to_write() ensures that at least one item + * gets successfully written even when busy condition is encountered. + * These assertions shuld hold when next_address_on_target overflows. */ + assert(next_addr_on_target - addr_on_target > 0); + assert(next_addr_on_target - args.address <= (target_addr_t)args.size * args.count); } - if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) - riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); - riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size); + return write_memory_progbuf_teardown(target) == ERROR_OK ? + mem_access_result(MEM_ACCESS_OK) : + mem_access_result(MEM_ACCESS_FAILED_PROGBUF_TEARDOWN_FAILED); +} - result = riscv_program_ebreak(&program); - if (result != ERROR_OK) - goto error; - riscv_program_write(&program); +static struct mem_access_result +write_memory_progbuf(struct target *target, const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_write(args)); - riscv_addr_t cur_addr = address; - riscv_addr_t fin_addr = address + (count * size); - bool setup_needed = true; - LOG_DEBUG("writing until final address 0x%016" PRIx64, fin_addr); - while (cur_addr < fin_addr) { - LOG_DEBUG("transferring burst starting at address 0x%016" PRIx64, - cur_addr); + struct mem_access_result result = write_memory_progbuf_inner(target, args); - struct riscv_batch *batch = riscv_batch_alloc( - target, - 32, - info->dmi_busy_delay + info->ac_busy_delay); - if (!batch) - goto error; - - /* To write another word, we put it in S1 and execute the program. */ - unsigned int start = (cur_addr - address) / size; - for (unsigned int i = start; i < count; ++i) { - unsigned int offset = size * i; - const uint8_t *t_buffer = buffer + offset; - - uint64_t value = buf_get_u64(t_buffer, 0, 8 * size); - - log_memory_access(address + offset, value, size, false); - cur_addr += size; - - if (setup_needed) { - result = register_write_direct(target, GDB_REGNO_S0, - address + offset); - if (result != ERROR_OK) { - riscv_batch_free(batch); - goto error; - } - - /* Write value. */ - if (size > 4) - dmi_write(target, DM_DATA1, value >> 32); - dmi_write(target, DM_DATA0, value); - - /* Write and execute command that moves value into S1 and - * executes program buffer. */ - uint32_t command = access_register_command(target, - GDB_REGNO_S1, riscv_xlen(target), - AC_ACCESS_REGISTER_POSTEXEC | - AC_ACCESS_REGISTER_TRANSFER | - AC_ACCESS_REGISTER_WRITE); - result = execute_abstract_command(target, command); - if (result != ERROR_OK) { - riscv_batch_free(batch); - goto error; - } - - /* Turn on autoexec */ - dmi_write(target, DM_ABSTRACTAUTO, - 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); - - setup_needed = false; - } else { - if (size > 4) - riscv_batch_add_dmi_write(batch, DM_DATA1, value >> 32); - riscv_batch_add_dmi_write(batch, DM_DATA0, value); - if (riscv_batch_full(batch)) - break; - } - } - - result = batch_run(target, batch); - riscv_batch_free(batch); - if (result != ERROR_OK) - goto error; - - /* Note that if the scan resulted in a Busy DMI response, it - * is this read to abstractcs that will cause the dmi_busy_delay - * to be incremented if necessary. */ - - uint32_t abstractcs; - bool dmi_busy_encountered; - result = dmi_op(target, &abstractcs, &dmi_busy_encountered, - DMI_OP_READ, DM_ABSTRACTCS, 0, false, true); - if (result != ERROR_OK) - goto error; - while (get_field(abstractcs, DM_ABSTRACTCS_BUSY)) - if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) - return ERROR_FAIL; - info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR); - if (info->cmderr == CMDERR_NONE && !dmi_busy_encountered) { - LOG_DEBUG("successful (partial?) memory write"); - } else if (info->cmderr == CMDERR_BUSY || dmi_busy_encountered) { - if (info->cmderr == CMDERR_BUSY) - LOG_DEBUG("Memory write resulted in abstract command busy response."); - else if (dmi_busy_encountered) - LOG_DEBUG("Memory write resulted in DMI busy response."); - riscv013_clear_abstract_error(target); - increase_ac_busy_delay(target); - - dmi_write(target, DM_ABSTRACTAUTO, 0); - result = register_read_direct(target, &cur_addr, GDB_REGNO_S0); - if (result != ERROR_OK) - goto error; - setup_needed = true; - } else { - LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs); - riscv013_clear_abstract_error(target); - result = ERROR_FAIL; - goto error; - } - } - -error: - dmi_write(target, DM_ABSTRACTAUTO, 0); - - if (register_write_direct(target, GDB_REGNO_S1, s1) != ERROR_OK) - return ERROR_FAIL; - if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) - return ERROR_FAIL; - - /* Restore MSTATUS */ - if (mstatus != mstatus_old) - if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) - return ERROR_FAIL; - - if (execute_fence(target) != ERROR_OK) - return ERROR_FAIL; + if (execute_autofence(target) != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED_FENCE_EXEC_FAILED); return result; } -static int write_memory(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, const uint8_t *buffer) +static bool riscv013_get_impebreak(const struct target *target) { - if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) { - LOG_ERROR("BUG: Unsupported size for memory write: %d", size); - return ERROR_FAIL; - } + RISCV013_INFO(r); + return r->impebreak; +} - int ret = ERROR_FAIL; - RISCV_INFO(r); - RISCV013_INFO(info); - - char *progbuf_result = "disabled"; - char *sysbus_result = "disabled"; - char *abstract_result = "disabled"; - - for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { - int method = r->mem_access_methods[i]; - - if (method == RISCV_MEM_ACCESS_PROGBUF) { - if (mem_should_skip_progbuf(target, address, size, false, &progbuf_result)) - continue; - - ret = write_memory_progbuf(target, address, size, count, buffer); - - if (ret != ERROR_OK) - progbuf_result = "failed"; - } else if (method == RISCV_MEM_ACCESS_SYSBUS) { - if (mem_should_skip_sysbus(target, address, size, 0, false, &sysbus_result)) - continue; - - if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0) - ret = write_memory_bus_v0(target, address, size, count, buffer); - else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1) - ret = write_memory_bus_v1(target, address, size, count, buffer); - - if (ret != ERROR_OK) - sysbus_result = "failed"; - } else if (method == RISCV_MEM_ACCESS_ABSTRACT) { - if (mem_should_skip_abstract(target, address, size, 0, false, &abstract_result)) - continue; - - ret = write_memory_abstract(target, address, size, count, buffer); - - if (ret != ERROR_OK) - abstract_result = "failed"; - } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED) - /* No further mem access method to try. */ - break; - - log_mem_access_result(target, ret == ERROR_OK, method, false); - - if (ret == ERROR_OK) - return ret; - } - - LOG_ERROR("Target %s: Failed to write memory (addr=0x%" PRIx64 ")", target_name(target), address); - LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); - return ret; +static unsigned int riscv013_get_progbufsize(const struct target *target) +{ + RISCV013_INFO(r); + return r->progbufsize; } static int arch_state(struct target *target) @@ -4057,144 +5090,139 @@ struct target_type riscv013_target = { .assert_reset = assert_reset, .deassert_reset = deassert_reset, - .write_memory = write_memory, - .arch_state = arch_state }; /*** 0.13-specific implementations of various RISC-V helper functions. ***/ -static int riscv013_get_register(struct target *target, - riscv_reg_t *value, int rid) +int riscv013_get_register(struct target *target, + riscv_reg_t *value, enum gdb_regno rid) { - LOG_DEBUG("[%s] reading register %s", target_name(target), - gdb_regno_name(rid)); - - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - - int result = ERROR_OK; - if (rid == GDB_REGNO_PC) { - /* TODO: move this into riscv.c. */ - result = register_read(target, value, GDB_REGNO_DPC); - LOG_DEBUG("[%d] read PC from DPC: 0x%" PRIx64, target->coreid, *value); - } else if (rid == GDB_REGNO_PRIV) { + /* It would be beneficial to move this redirection to the + * version-independent section, but there is a conflict: + * `dcsr[5]` is `dcsr.v` in current spec, but it is `dcsr.debugint` in 0.11. + */ + if (rid == GDB_REGNO_PRIV) { uint64_t dcsr; - /* TODO: move this into riscv.c. */ - result = register_read(target, &dcsr, GDB_REGNO_DCSR); + if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; *value = set_field(0, VIRT_PRIV_V, get_field(dcsr, CSR_DCSR_V)); *value = set_field(*value, VIRT_PRIV_PRV, get_field(dcsr, CSR_DCSR_PRV)); - } else { - result = register_read(target, value, rid); - if (result != ERROR_OK) - *value = -1; + return ERROR_OK; } - return result; -} + LOG_TARGET_DEBUG(target, "reading register %s", riscv_reg_gdb_regno_name(target, rid)); -static int riscv013_set_register(struct target *target, int rid, uint64_t value) -{ - riscv013_select_current_hart(target); - LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s", - target->coreid, value, gdb_regno_name(rid)); + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; - if (rid <= GDB_REGNO_XPR31) { - return register_write_direct(target, rid, value); - } else if (rid == GDB_REGNO_PC) { - LOG_DEBUG("[%d] writing PC to DPC: 0x%" PRIx64, target->coreid, value); - register_write_direct(target, GDB_REGNO_DPC, value); - uint64_t actual_value; - register_read_direct(target, &actual_value, GDB_REGNO_DPC); - LOG_DEBUG("[%d] actual DPC written: 0x%016" PRIx64, target->coreid, actual_value); - if (value != actual_value) { - LOG_ERROR("Written PC (0x%" PRIx64 ") does not match read back " - "value (0x%" PRIx64 ")", value, actual_value); - return ERROR_FAIL; - } - } else if (rid == GDB_REGNO_PRIV) { - uint64_t dcsr; - register_read(target, &dcsr, GDB_REGNO_DCSR); - dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV)); - dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V)); - return register_write_direct(target, GDB_REGNO_DCSR, dcsr); - } else { - return register_write_direct(target, rid, value); + if (register_read_direct(target, value, rid) != ERROR_OK) { + *value = -1; + return ERROR_FAIL; } return ERROR_OK; } -static int riscv013_select_current_hart(struct target *target) +int riscv013_set_register(struct target *target, enum gdb_regno rid, + riscv_reg_t value) { - RISCV_INFO(r); + LOG_TARGET_DEBUG(target, "writing 0x%" PRIx64 " to register %s", + value, riscv_reg_gdb_regno_name(target, rid)); + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + return register_write_direct(target, rid, value); +} + +static int dm013_select_hart(struct target *target, int hart_index) +{ dm013_info_t *dm = get_dm(target); if (!dm) return ERROR_FAIL; - if (r->current_hartid == dm->current_hartid) + if (hart_index == dm->current_hartid) return ERROR_OK; - uint32_t dmcontrol; - /* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */ - if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) + /* `hartsel` should not be changed if `abstractcs.busy` is set. */ + int result = wait_for_idle_if_needed(target); + if (result != ERROR_OK) + return result; + + uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE; + dmcontrol = set_dmcontrol_hartsel(dmcontrol, hart_index); + if (dm_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK) { + /* Who knows what the state is? */ + dm->current_hartid = HART_INDEX_UNKNOWN; return ERROR_FAIL; - dmcontrol = set_hartsel(dmcontrol, r->current_hartid); - int result = dmi_write(target, DM_DMCONTROL, dmcontrol); - dm->current_hartid = r->current_hartid; - return result; + } + dm->current_hartid = hart_index; + return ERROR_OK; } /* Select all harts that were prepped and that are selectable, clearing the * prepped flag on the harts that actually were selected. */ -static int select_prepped_harts(struct target *target, bool *use_hasel) +static int select_prepped_harts(struct target *target) { + RISCV_INFO(r); dm013_info_t *dm = get_dm(target); if (!dm) return ERROR_FAIL; if (!dm->hasel_supported) { - RISCV_INFO(r); r->prepped = false; - *use_hasel = false; - return ERROR_OK; + return dm013_select_target(target); } assert(dm->hart_count); unsigned int hawindow_count = (dm->hart_count + 31) / 32; - uint32_t hawindow[hawindow_count]; - - memset(hawindow, 0, sizeof(uint32_t) * hawindow_count); + uint32_t *hawindow = calloc(hawindow_count, sizeof(uint32_t)); + if (!hawindow) + return ERROR_FAIL; target_list_t *entry; unsigned int total_selected = 0; + unsigned int selected_index = 0; list_for_each_entry(entry, &dm->target_list, list) { struct target *t = entry->target; - struct riscv_info *r = riscv_info(t); - riscv013_info_t *info = get_info(t); - unsigned int index = info->index; - LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, r->prepped); - r->selected = r->prepped; - if (r->prepped) { + struct riscv_info *info = riscv_info(t); + riscv013_info_t *info_013 = get_info(t); + unsigned int index = info_013->index; + LOG_TARGET_DEBUG(target, "index=%d, prepped=%d", index, info->prepped); + if (info->prepped) { + info_013->selected = true; hawindow[index / 32] |= 1 << (index % 32); - r->prepped = false; + info->prepped = false; total_selected++; + selected_index = index; } - index++; } - /* Don't use hasel if we only need to talk to one hart. */ - if (total_selected <= 1) { - *use_hasel = false; - return ERROR_OK; + if (total_selected == 0) { + LOG_TARGET_ERROR(target, "No harts were prepped!"); + free(hawindow); + return ERROR_FAIL; + } else if (total_selected == 1) { + /* Don't use hasel if we only need to talk to one hart. */ + free(hawindow); + return dm013_select_hart(target, selected_index); + } + + if (dm013_select_hart(target, HART_INDEX_MULTIPLE) != ERROR_OK) { + free(hawindow); + return ERROR_FAIL; } for (unsigned int i = 0; i < hawindow_count; i++) { - if (dmi_write(target, DM_HAWINDOWSEL, i) != ERROR_OK) + if (dm_write(target, DM_HAWINDOWSEL, i) != ERROR_OK) { + free(hawindow); return ERROR_FAIL; - if (dmi_write(target, DM_HAWINDOW, hawindow[i]) != ERROR_OK) + } + if (dm_write(target, DM_HAWINDOW, hawindow[i]) != ERROR_OK) { + free(hawindow); return ERROR_FAIL; + } } - *use_hasel = true; + free(hawindow); return ERROR_OK; } @@ -4205,72 +5233,107 @@ static int riscv013_halt_prep(struct target *target) static int riscv013_halt_go(struct target *target) { - bool use_hasel = false; - if (select_prepped_harts(target, &use_hasel) != ERROR_OK) + dm013_info_t *dm = get_dm(target); + if (!dm) return ERROR_FAIL; - RISCV_INFO(r); - LOG_DEBUG("halting hart %d", r->current_hartid); + if (select_prepped_harts(target) != ERROR_OK) + return ERROR_FAIL; + + LOG_TARGET_DEBUG(target, "halting hart"); + + /* `haltreq` should not be issued if `abstractcs.busy` is set. */ + int result = wait_for_idle_if_needed(target); + if (result != ERROR_OK) + return result; /* Issue the halt command, and then wait for the current hart to halt. */ uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_HALTREQ; - if (use_hasel) - dmcontrol |= DM_DMCONTROL_HASEL; - dmcontrol = set_hartsel(dmcontrol, r->current_hartid); - dmi_write(target, DM_DMCONTROL, dmcontrol); - for (size_t i = 0; i < 256; ++i) - if (riscv_is_halted(target)) - break; - - if (!riscv_is_halted(target)) { - uint32_t dmstatus; + dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid); + dm_write(target, DM_DMCONTROL, dmcontrol); + uint32_t dmstatus; + for (size_t i = 0; i < 256; ++i) { if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; - if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) + /* When no harts are running, there's no point in continuing this loop. */ + if (!get_field(dmstatus, DM_DMSTATUS_ANYRUNNING)) + break; + } + + /* We declare success if no harts are running. One or more of them may be + * unavailable, though. */ + + if ((get_field(dmstatus, DM_DMSTATUS_ANYRUNNING))) { + if (dm_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) return ERROR_FAIL; - LOG_ERROR("unable to halt hart %d", r->current_hartid); - LOG_ERROR(" dmcontrol=0x%08x", dmcontrol); - LOG_ERROR(" dmstatus =0x%08x", dmstatus); + LOG_TARGET_ERROR(target, "Unable to halt. dmcontrol=0x%08x, dmstatus=0x%08x", + dmcontrol, dmstatus); return ERROR_FAIL; } dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HALTREQ, 0); - dmi_write(target, DM_DMCONTROL, dmcontrol); + dm_write(target, DM_DMCONTROL, dmcontrol); - if (use_hasel) { + if (dm->current_hartid == HART_INDEX_MULTIPLE) { target_list_t *entry; - dm013_info_t *dm = get_dm(target); - if (!dm) - return ERROR_FAIL; list_for_each_entry(entry, &dm->target_list, list) { struct target *t = entry->target; - t->state = TARGET_HALTED; - if (t->debug_reason == DBG_REASON_NOTHALTED) - t->debug_reason = DBG_REASON_DBGRQ; + uint32_t t_dmstatus; + if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED) || + get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { + /* All harts are either halted or unavailable. No + * need to read dmstatus for each hart. */ + t_dmstatus = dmstatus; + } else { + /* Only some harts were halted/unavailable. Read + * dmstatus for this one to see what its status + * is. */ + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + if (dm_read(target, &t_dmstatus, DM_DMSTATUS) != ERROR_OK) + return ERROR_FAIL; + } + /* Set state for the current target based on its dmstatus. */ + if (get_field(t_dmstatus, DM_DMSTATUS_ALLHALTED)) { + t->state = TARGET_HALTED; + if (t->debug_reason == DBG_REASON_NOTHALTED) + t->debug_reason = DBG_REASON_DBGRQ; + } else if (get_field(t_dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { + t->state = TARGET_UNAVAILABLE; + } + } + + } else { + /* Set state for the current target based on its dmstatus. */ + if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED)) { + target->state = TARGET_HALTED; + if (target->debug_reason == DBG_REASON_NOTHALTED) + target->debug_reason = DBG_REASON_DBGRQ; + } else if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) { + target->state = TARGET_UNAVAILABLE; } } - /* The "else" case is handled in halt_go(). */ return ERROR_OK; } static int riscv013_resume_go(struct target *target) { - bool use_hasel = false; - if (select_prepped_harts(target, &use_hasel) != ERROR_OK) + if (select_prepped_harts(target) != ERROR_OK) return ERROR_FAIL; - return riscv013_step_or_resume_current_hart(target, false, use_hasel); + return riscv013_step_or_resume_current_hart(target, false); } static int riscv013_step_current_hart(struct target *target) { - return riscv013_step_or_resume_current_hart(target, true, false); + return riscv013_step_or_resume_current_hart(target, true); } static int riscv013_resume_prep(struct target *target) { + assert(target->state == TARGET_HALTED); return riscv013_on_step_or_resume(target, false); } @@ -4279,95 +5342,80 @@ static int riscv013_on_step(struct target *target) return riscv013_on_step_or_resume(target, true); } -static int riscv013_on_halt(struct target *target) -{ - return ERROR_OK; -} - -static bool riscv013_is_halted(struct target *target) -{ - uint32_t dmstatus; - if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) - return false; - if (get_field(dmstatus, DM_DMSTATUS_ANYUNAVAIL)) - LOG_ERROR("Hart %d is unavailable.", riscv_current_hartid(target)); - if (get_field(dmstatus, DM_DMSTATUS_ANYNONEXISTENT)) - LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target)); - if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) { - int hartid = riscv_current_hartid(target); - LOG_INFO("Hart %d unexpectedly reset!", hartid); - /* TODO: Can we make this more obvious to eg. a gdb user? */ - uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | - DM_DMCONTROL_ACKHAVERESET; - dmcontrol = set_hartsel(dmcontrol, hartid); - /* If we had been halted when we reset, request another halt. If we - * ended up running out of reset, then the user will (hopefully) get a - * message that a reset happened, that the target is running, and then - * that it is halted again once the request goes through. - */ - if (target->state == TARGET_HALTED) - dmcontrol |= DM_DMCONTROL_HALTREQ; - dmi_write(target, DM_DMCONTROL, dmcontrol); - } - return get_field(dmstatus, DM_DMSTATUS_ALLHALTED); -} - static enum riscv_halt_reason riscv013_halt_reason(struct target *target) { riscv_reg_t dcsr; - int result = register_read(target, &dcsr, GDB_REGNO_DCSR); + int result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR); if (result != ERROR_OK) return RISCV_HALT_UNKNOWN; - LOG_DEBUG("dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE)); + LOG_TARGET_DEBUG(target, "dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE)); switch (get_field(dcsr, CSR_DCSR_CAUSE)) { - case CSR_DCSR_CAUSE_SWBP: - return RISCV_HALT_BREAKPOINT; + case CSR_DCSR_CAUSE_EBREAK: + return RISCV_HALT_EBREAK; case CSR_DCSR_CAUSE_TRIGGER: /* We could get here before triggers are enumerated if a trigger was * already set when we connected. Force enumeration now, which has the * side effect of clearing any triggers we did not set. */ riscv_enumerate_triggers(target); - LOG_DEBUG("{%d} halted because of trigger", target->coreid); + LOG_TARGET_DEBUG(target, "halted because of trigger"); return RISCV_HALT_TRIGGER; case CSR_DCSR_CAUSE_STEP: return RISCV_HALT_SINGLESTEP; - case CSR_DCSR_CAUSE_DEBUGINT: - case CSR_DCSR_CAUSE_HALT: + case CSR_DCSR_CAUSE_HALTREQ: + case CSR_DCSR_CAUSE_RESETHALTREQ: return RISCV_HALT_INTERRUPT; case CSR_DCSR_CAUSE_GROUP: return RISCV_HALT_GROUP; } - LOG_ERROR("Unknown DCSR cause field: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE)); - LOG_ERROR(" dcsr=0x%016lx", (long)dcsr); + LOG_TARGET_ERROR(target, "Unknown DCSR cause field: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE)); + LOG_TARGET_ERROR(target, " dcsr=0x%" PRIx32, (uint32_t)dcsr); return RISCV_HALT_UNKNOWN; } -int riscv013_write_debug_buffer(struct target *target, unsigned int index, riscv_insn_t data) +static int riscv013_write_progbuf(struct target *target, unsigned int index, riscv_insn_t data) { + assert(index < RISCV013_MAX_PROGBUF_SIZE); + dm013_info_t *dm = get_dm(target); if (!dm) return ERROR_FAIL; + if (dm->progbuf_cache[index] != data) { - if (dmi_write(target, DM_PROGBUF0 + index, data) != ERROR_OK) + if (dm_write(target, DM_PROGBUF0 + index, data) != ERROR_OK) return ERROR_FAIL; dm->progbuf_cache[index] = data; } else { - LOG_DEBUG("cache hit for 0x%" PRIx32 " @%d", data, index); + LOG_TARGET_DEBUG(target, "Cache hit for 0x%" PRIx32 " @%d", data, index); } return ERROR_OK; } -riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned int index) +static riscv_insn_t riscv013_read_progbuf(struct target *target, unsigned int index) { uint32_t value; - dmi_read(target, &value, DM_PROGBUF0 + index); - return value; + if (dm_read(target, &value, DM_PROGBUF0 + index) == ERROR_OK) + return value; + else + return 0; } -int riscv013_execute_debug_buffer(struct target *target) +static int riscv013_invalidate_cached_progbuf(struct target *target) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) { + LOG_TARGET_DEBUG(target, "No DM is specified for the target"); + return ERROR_FAIL; + } + + LOG_TARGET_DEBUG(target, "Invalidating progbuf cache"); + memset(dm->progbuf_cache, 0, sizeof(dm->progbuf_cache)); + return ERROR_OK; +} + +static int riscv013_execute_progbuf(struct target *target, uint32_t *cmderr) { uint32_t run_program = 0; run_program = set_field(run_program, AC_ACCESS_REGISTER_AARSIZE, 2); @@ -4375,82 +5423,80 @@ int riscv013_execute_debug_buffer(struct target *target) run_program = set_field(run_program, AC_ACCESS_REGISTER_TRANSFER, 0); run_program = set_field(run_program, AC_ACCESS_REGISTER_REGNO, 0x1000); - return execute_abstract_command(target, run_program); + return riscv013_execute_abstract_command(target, run_program, cmderr); } -void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d) +static void riscv013_fill_dmi_write(const struct target *target, uint8_t *buf, uint32_t a, uint32_t d) { RISCV013_INFO(info); - buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_WRITE); - buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, d); - buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a); + buf_set_u32(buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_WRITE); + buf_set_u32(buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, d); + buf_set_u32(buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a); } -void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a) +static void riscv013_fill_dmi_read(const struct target *target, uint8_t *buf, uint32_t a) { RISCV013_INFO(info); - buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_READ); - buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0); - buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a); + buf_set_u32(buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_READ); + buf_set_u32(buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0); + buf_set_u32(buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a); } -void riscv013_fill_dmi_nop_u64(struct target *target, char *buf) +static void riscv013_fill_dm_nop(const struct target *target, uint8_t *buf) { RISCV013_INFO(info); - buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_NOP); - buf_set_u64((unsigned char *)buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0); - buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, 0); + buf_set_u32(buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_NOP); + buf_set_u32(buf, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, 0); + buf_set_u32(buf, DTM_DMI_ADDRESS_OFFSET, info->abits, 0); } -int riscv013_dmi_write_u64_bits(struct target *target) +static unsigned int riscv013_get_dmi_address_bits(const struct target *target) { RISCV013_INFO(info); - return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH; -} - -static int maybe_execute_fence_i(struct target *target) -{ - if (has_sufficient_progbuf(target, 3)) - return execute_fence(target); - return ERROR_OK; + return info->abits; } /* Helper Functions. */ static int riscv013_on_step_or_resume(struct target *target, bool step) { - if (maybe_execute_fence_i(target) != ERROR_OK) + if (has_sufficient_progbuf(target, 2)) + if (execute_autofence(target) != ERROR_OK) + return ERROR_FAIL; + + if (set_dcsr_ebreak(target, step) != ERROR_OK) return ERROR_FAIL; - /* We want to twiddle some bits in the debug CSR so debugging works. */ - riscv_reg_t dcsr; - int result = register_read(target, &dcsr, GDB_REGNO_DCSR); - if (result != ERROR_OK) - return result; - dcsr = set_field(dcsr, CSR_DCSR_STEP, step); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, riscv_ebreakm); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, riscv_ebreaks); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, riscv_ebreaku); - return riscv_set_register(target, GDB_REGNO_DCSR, dcsr); + if (riscv_reg_flush_all(target) != ERROR_OK) + return ERROR_FAIL; + return ERROR_OK; } static int riscv013_step_or_resume_current_hart(struct target *target, - bool step, bool use_hasel) + bool step) { - RISCV_INFO(r); - LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step); - if (!riscv_is_halted(target)) { - LOG_ERROR("Hart %d is not halted!", r->current_hartid); + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Hart is not halted!"); return ERROR_FAIL; } + LOG_TARGET_DEBUG(target, "resuming (operation=%s)", + step ? "single-step" : "resume"); + + if (riscv_reg_flush_all(target) != ERROR_OK) + return ERROR_FAIL; + + riscv_reg_cache_invalidate_all(target); + + dm013_info_t *dm = get_dm(target); /* Issue the resume command, and then wait for the current hart to resume. */ uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_RESUMEREQ; - if (use_hasel) - dmcontrol |= DM_DMCONTROL_HASEL; - dmcontrol = set_hartsel(dmcontrol, r->current_hartid); - dmi_write(target, DM_DMCONTROL, dmcontrol); + dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid); + /* `resumereq` should not be issued if `abstractcs.busy` is set. */ + int result = wait_for_idle_if_needed(target); + if (result != ERROR_OK) + return result; + dm_write(target, DM_DMCONTROL, dmcontrol); - dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HASEL, 0); dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0); uint32_t dmstatus; @@ -4458,49 +5504,49 @@ static int riscv013_step_or_resume_current_hart(struct target *target, usleep(10); if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; + if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) + return ERROR_FAIL; if (get_field(dmstatus, DM_DMSTATUS_ALLRESUMEACK) == 0) continue; if (step && get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0) continue; - dmi_write(target, DM_DMCONTROL, dmcontrol); + dm_write(target, DM_DMCONTROL, dmcontrol); return ERROR_OK; } - dmi_write(target, DM_DMCONTROL, dmcontrol); + LOG_TARGET_ERROR(target, "Failed to %s. dmstatus=0x%08x", + step ? "single-step" : "resume", dmstatus); + + dm_write(target, DM_DMCONTROL, dmcontrol); + LOG_TARGET_ERROR(target, + " cancelling the resume request (dmcontrol.resumereq <- 0)"); - LOG_ERROR("unable to resume hart %d", r->current_hartid); if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; - LOG_ERROR(" dmstatus =0x%08x", dmstatus); + + LOG_TARGET_ERROR(target, " dmstatus after cancellation=0x%08x", dmstatus); if (step) { - LOG_ERROR(" was stepping, halting"); - riscv_halt(target); + LOG_TARGET_ERROR(target, + " trying to recover from a failed single-step, by requesting halt"); + if (riscv_halt(target) == ERROR_OK) + LOG_TARGET_ERROR(target, " halt completed after failed single-step"); + else + LOG_TARGET_ERROR(target, " could not halt, something is wrong with the taget"); + // TODO: returning ERROR_OK is questionable, this code needs to be revised return ERROR_OK; } return ERROR_FAIL; } -void riscv013_clear_abstract_error(struct target *target) +static int riscv013_clear_abstract_error(struct target *target) { - /* Wait for busy to go away. */ - time_t start = time(NULL); uint32_t abstractcs; - dmi_read(target, &abstractcs, DM_ABSTRACTCS); - while (get_field(abstractcs, DM_ABSTRACTCS_BUSY)) { - dmi_read(target, &abstractcs, DM_ABSTRACTCS); - - if (time(NULL) - start > riscv_command_timeout_sec) { - LOG_ERROR("abstractcs.busy is not going low after %d seconds " - "(abstractcs=0x%x). The target is either really slow or " - "broken. You could increase the timeout with riscv " - "set_command_timeout_sec.", - riscv_command_timeout_sec, abstractcs); - break; - } - } - /* Clear the error status. */ - dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); + int result = wait_for_idle(target, &abstractcs); + /* Clear the error status, even if busy is still set. */ + if (dm_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR) != ERROR_OK) + result = ERROR_FAIL; + return result; } diff --git a/src/target/riscv/riscv-013.h b/src/target/riscv/riscv-013.h new file mode 100644 index 000000000..ca2d4aefc --- /dev/null +++ b/src/target/riscv/riscv-013.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_RISCV_013_H +#define OPENOCD_TARGET_RISCV_RISCV_013_H + +#include "riscv.h" + +/* TODO: These functions should be replaced here by access methods that can be + * reused by other modules (e.g. a function writing an abstract commands, a + * function filling/executing program buffer, etc.), while the specifics on how + * to use these general-purpose version-specific methods to get a register's + * value will be in `riscv-013_reg.c`. + */ +int riscv013_get_register(struct target *target, + riscv_reg_t *value, enum gdb_regno rid); +int riscv013_get_register_buf(struct target *target, uint8_t *value, + enum gdb_regno regno); +int riscv013_set_register(struct target *target, enum gdb_regno rid, + riscv_reg_t value); +int riscv013_set_register_buf(struct target *target, enum gdb_regno regno, + const uint8_t *value); +uint32_t riscv013_access_register_command(struct target *target, uint32_t number, + unsigned int size, uint32_t flags); +int riscv013_execute_abstract_command(struct target *target, uint32_t command, + uint32_t *cmderr); + +#endif /* OPENOCD_TARGET_RISCV_RISCV_013_H */ diff --git a/src/target/riscv/riscv-013_reg.c b/src/target/riscv/riscv-013_reg.c new file mode 100644 index 000000000..b2b1a921f --- /dev/null +++ b/src/target/riscv/riscv-013_reg.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "riscv-013_reg.h" +#include "field_helpers.h" + +#include "riscv_reg.h" +#include "riscv_reg_impl.h" +#include "riscv-013.h" +#include "debug_defines.h" +#include + +static int riscv013_reg_get(struct reg *reg) +{ + struct target *target = riscv_reg_impl_get_target(reg); + + /* TODO: Hack to deal with gdb that thinks these registers still exist. */ + if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 && + riscv_supports_extension(target, 'E')) { + buf_set_u64(reg->value, 0, reg->size, 0); + return ERROR_OK; + } + + if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) { + if (riscv013_get_register_buf(target, reg->value, reg->number) != ERROR_OK) + return ERROR_FAIL; + + reg->valid = riscv_reg_impl_gdb_regno_cacheable(reg->number, /* is write? */ false); + } else { + uint64_t value; + int result = riscv_reg_get(target, &value, reg->number); + if (result != ERROR_OK) + return result; + buf_set_u64(reg->value, 0, reg->size, value); + } + char *str = buf_to_hex_str(reg->value, reg->size); + LOG_TARGET_DEBUG(target, "Read 0x%s from %s (valid=%d).", str, reg->name, + reg->valid); + free(str); + return ERROR_OK; +} + +static int riscv013_reg_set(struct reg *reg, uint8_t *buf) +{ + struct target *target = riscv_reg_impl_get_target(reg); + + char *str = buf_to_hex_str(buf, reg->size); + LOG_TARGET_DEBUG(target, "Write 0x%s to %s (valid=%d).", str, reg->name, + reg->valid); + free(str); + + /* TODO: Hack to deal with gdb that thinks these registers still exist. */ + if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 && + riscv_supports_extension(target, 'E') && + buf_get_u64(buf, 0, reg->size) == 0) + return ERROR_OK; + + if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) { + if (riscv013_set_register_buf(target, reg->number, buf) != ERROR_OK) + return ERROR_FAIL; + + memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8)); + reg->valid = riscv_reg_impl_gdb_regno_cacheable(reg->number, /* is write? */ true); + } else { + const riscv_reg_t value = buf_get_u64(buf, 0, reg->size); + if (riscv_reg_set(target, reg->number, value) != ERROR_OK) + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static const struct reg_arch_type *riscv013_gdb_regno_reg_type(uint32_t regno) +{ + static const struct reg_arch_type riscv013_reg_type = { + .get = riscv013_reg_get, + .set = riscv013_reg_set + }; + return &riscv013_reg_type; +} + +static int init_cache_entry(struct target *target, uint32_t regno) +{ + struct reg * const reg = riscv_reg_impl_cache_entry(target, regno); + if (riscv_reg_impl_is_initialized(reg)) + return ERROR_OK; + return riscv_reg_impl_init_cache_entry(target, regno, + riscv_reg_impl_gdb_regno_exist(target, regno), + riscv013_gdb_regno_reg_type(regno)); +} + +/** + * Some registers are optional (e.g. "misa"). For such registers it is first + * assumed they exist (via "assume_reg_exist()"), then the read is attempted + * (via the usual "riscv_reg_get()") and if the read fails, the register is + * marked as non-existing (via "riscv_reg_impl_set_exist()"). + */ +static int assume_reg_exist(struct target *target, uint32_t regno) +{ + return riscv_reg_impl_init_cache_entry(target, regno, + /* exist */ true, riscv013_gdb_regno_reg_type(regno)); +} + +static int examine_xlen(struct target *target) +{ + RISCV_INFO(r); + unsigned int cmderr; + + const uint32_t command = riscv013_access_register_command(target, + GDB_REGNO_S0, /* size */ 64, AC_ACCESS_REGISTER_TRANSFER); + int res = riscv013_execute_abstract_command(target, command, &cmderr); + if (res == ERROR_OK) { + r->xlen = 64; + return ERROR_OK; + } + if (res == ERROR_TIMEOUT_REACHED) + return ERROR_FAIL; + r->xlen = 32; + + return ERROR_OK; +} + +static int examine_vlenb(struct target *target) +{ + RISCV_INFO(r); + + /* Reading "vlenb" requires "mstatus.vs" to be set, so "mstatus" should + * be accessible.*/ + int res = init_cache_entry(target, GDB_REGNO_MSTATUS); + if (res != ERROR_OK) + return res; + + res = assume_reg_exist(target, GDB_REGNO_VLENB); + if (res != ERROR_OK) + return res; + + riscv_reg_t vlenb_val; + if (riscv_reg_get(target, &vlenb_val, GDB_REGNO_VLENB) != ERROR_OK) { + if (riscv_supports_extension(target, 'V')) + LOG_TARGET_WARNING(target, "Couldn't read vlenb; vector register access won't work."); + r->vlenb = 0; + return riscv_reg_impl_set_exist(target, GDB_REGNO_VLENB, false); + } + /* As defined by RISC-V V extension specification: + * https://github.com/riscv/riscv-v-spec/blob/2f68ef7256d6ec53e4d2bd7cb12862f406d64e34/v-spec.adoc?plain=1#L67-L72 */ + const unsigned int vlen_max = 65536; + const unsigned int vlenb_max = vlen_max / 8; + if (vlenb_val > vlenb_max) { + LOG_TARGET_WARNING(target, "'vlenb == %" PRIu64 + "' is greater than maximum allowed by specification (%u); vector register access won't work.", + vlenb_val, vlenb_max); + r->vlenb = 0; + return ERROR_OK; + } + assert(vlenb_max <= UINT_MAX); + r->vlenb = (unsigned int)vlenb_val; + + LOG_TARGET_INFO(target, "Vector support with vlenb=%u", r->vlenb); + return ERROR_OK; +} + +enum misa_mxl { + MISA_MXL_INVALID = 0, + MISA_MXL_32 = 1, + MISA_MXL_64 = 2, + MISA_MXL_128 = 3 +}; + +unsigned int mxl_to_xlen(enum misa_mxl mxl) +{ + switch (mxl) { + case MISA_MXL_32: + return 32; + case MISA_MXL_64: + return 64; + case MISA_MXL_128: + return 128; + case MISA_MXL_INVALID: + assert(0); + } + return 0; +} + +static int check_misa_mxl(const struct target *target) +{ + RISCV_INFO(r); + + if (r->misa == 0) { + LOG_TARGET_WARNING(target, "'misa' register is read as zero." + "OpenOCD will not be able to determine some hart's capabilities."); + return ERROR_OK; + } + const unsigned int dxlen = riscv_xlen(target); + assert(dxlen <= sizeof(riscv_reg_t) * CHAR_BIT); + assert(dxlen >= 2); + const riscv_reg_t misa_mxl_mask = (riscv_reg_t)0x3 << (dxlen - 2); + const unsigned int mxl = get_field(r->misa, misa_mxl_mask); + if (mxl == MISA_MXL_INVALID) { + /* This is not an error! + * Imagine the platform that: + * - Has no abstract access to CSRs, so that CSRs are read + * through Program Buffer via "csrr" instruction. + * - Complies to v1.10 of the Priveleged Spec, so that misa.mxl + * is WARL and MXLEN may be chainged. + * https://github.com/riscv/riscv-isa-manual/commit/9a7dd2fe29011587954560b5dcf1875477b27ad8 + * - DXLEN == MXLEN on reset == 64. + * In a following scenario: + * - misa.mxl was written, so that MXLEN is 32. + * - Debugger connects to the target. + * - Debugger observes DXLEN == 64. + * - Debugger reads misa: + * - Abstract access fails with "cmderr == not supported". + * - Access via Program Buffer involves reading "misa" to an + * "xreg" via "csrr", so that the "xreg" is filled with + * zero-extended value of "misa" (since "misa" is + * MXLEN-wide). + * - Debugger derives "misa.mxl" assumig "misa" is DXLEN-bit + * wide (64) while MXLEN is 32 and therefore erroneously + * assumes "misa.mxl" to be zero (invalid). + */ + LOG_TARGET_WARNING(target, "Detected DXLEN (%u) does not match " + "MXLEN: misa.mxl == 0, misa == 0x%" PRIx64 ".", + dxlen, r->misa); + return ERROR_OK; + } + const unsigned int mxlen = mxl_to_xlen(mxl); + if (dxlen < mxlen) { + LOG_TARGET_ERROR(target, + "MXLEN (%u) reported in misa.mxl field exceeds " + "the detected DXLEN (%u)", + mxlen, dxlen); + return ERROR_FAIL; + } + /* NOTE: + * The value of "misa.mxl" may stil not coincide with "xlen". + * "misa[26:XLEN-3]" bits are marked as WIRI in at least version 1.10 + * of RISC-V Priveleged Spec. Therefore, if "xlen" is erroneously + * assumed to be 32 when it actually is 64, "mxl" will be read from + * this WIRI field and may be equal to "MISA_MXL_32" by coincidence. + * This is not an issue though from the version 1.11 onward, since + * "misa[26:XLEN-3]" became WARL and equal to 0. + */ + + /* Display this as early as possible to help people who are using + * really slow simulators. */ + LOG_TARGET_DEBUG(target, " XLEN=%d, misa=0x%" PRIx64, riscv_xlen(target), r->misa); + return ERROR_OK; +} + +static int examine_misa(struct target *target) +{ + RISCV_INFO(r); + + int res = init_cache_entry(target, GDB_REGNO_MISA); + if (res != ERROR_OK) + return res; + + res = riscv_reg_get(target, &r->misa, GDB_REGNO_MISA); + if (res != ERROR_OK) + return res; + return check_misa_mxl(target); +} + +static int examine_mtopi(struct target *target) +{ + /* Assume the registers exist */ + int res = assume_reg_exist(target, GDB_REGNO_MTOPI); + if (res != ERROR_OK) + return res; + res = assume_reg_exist(target, GDB_REGNO_MTOPEI); + if (res != ERROR_OK) + return res; + + riscv_reg_t value; + if (riscv_reg_get(target, &value, GDB_REGNO_MTOPI) != ERROR_OK) { + res = riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPI, false); + if (res != ERROR_OK) + return res; + return riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPEI, false); + } + if (riscv_reg_get(target, &value, GDB_REGNO_MTOPEI) != ERROR_OK) { + LOG_TARGET_INFO(target, "S?aia detected without IMSIC"); + return riscv_reg_impl_set_exist(target, GDB_REGNO_MTOPEI, false); + } + LOG_TARGET_INFO(target, "S?aia detected with IMSIC"); + return ERROR_OK; +} + +/** + * This function assumes target's DM to be initialized (target is able to + * access DMs registers, execute program buffer, etc.) + */ +int riscv013_reg_examine_all(struct target *target) +{ + int res = riscv_reg_impl_init_cache(target); + if (res != ERROR_OK) + return res; + + init_shared_reg_info(target); + + assert(target->state == TARGET_HALTED); + + res = examine_xlen(target); + if (res != ERROR_OK) + return res; + + /* Reading CSRs may clobber "s0", "s1", so it should be possible to + * save them in cache. */ + res = init_cache_entry(target, GDB_REGNO_S0); + if (res != ERROR_OK) + return res; + res = init_cache_entry(target, GDB_REGNO_S1); + if (res != ERROR_OK) + return res; + + res = examine_misa(target); + if (res != ERROR_OK) + return res; + + res = examine_vlenb(target); + if (res != ERROR_OK) + return res; + + riscv_reg_impl_init_vector_reg_type(target); + + res = examine_mtopi(target); + if (res != ERROR_OK) + return res; + + for (uint32_t regno = 0; regno < target->reg_cache->num_regs; ++regno) { + res = init_cache_entry(target, regno); + if (res != ERROR_OK) + return res; + } + + res = riscv_reg_impl_expose_csrs(target); + if (res != ERROR_OK) + return res; + + riscv_reg_impl_hide_csrs(target); + + return ERROR_OK; +} + +/** + * This function is used to save the value of a register in cache. The register + * is marked as dirty, and writeback is delayed for as long as possible. + */ +int riscv013_reg_save(struct target *target, enum gdb_regno regid) +{ + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Can't save register %s on a hart that is not halted.", + riscv_reg_gdb_regno_name(target, regid)); + return ERROR_FAIL; + } + assert(riscv_reg_impl_gdb_regno_cacheable(regid, /* is write? */ false) && + "Only cacheable registers can be saved."); + + RISCV_INFO(r); + riscv_reg_t value; + if (!target->reg_cache) { + assert(!target_was_examined(target)); + /* To create register cache it is needed to examine the target first, + * therefore during examine, any changed register needs to be saved + * and restored manually. + */ + return ERROR_OK; + } + + struct reg *reg = riscv_reg_impl_cache_entry(target, regid); + + LOG_TARGET_DEBUG(target, "Saving %s", reg->name); + if (riscv_reg_get(target, &value, regid) != ERROR_OK) + return ERROR_FAIL; + + assert(reg->valid && + "The register is cacheable, so the cache entry must be valid now."); + /* Mark the register dirty. We assume that this function is called + * because the caller is about to mess with the underlying value of the + * register. */ + reg->dirty = true; + + r->last_activity = timeval_ms(); + + return ERROR_OK; +} diff --git a/src/target/riscv/riscv-013_reg.h b/src/target/riscv/riscv-013_reg.h new file mode 100644 index 000000000..e542a3501 --- /dev/null +++ b/src/target/riscv/riscv-013_reg.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_RISCV_013_REG_H +#define OPENOCD_TARGET_RISCV_RISCV_013_REG_H + +#include "target/target.h" +#include "gdb_regs.h" +#include "riscv.h" + +/** + * This file describes additional register cache interface available to the + * RISC-V Debug Specification v0.13+ targets. + */ + +/** + * This function assumes target is halted. + * After this function all registers can be safely accessed via functions + * described here and in `riscv_reg.h`. + */ +int riscv013_reg_examine_all(struct target *target); + +/** + * This function is used to save the value of a register in cache. The register + * is marked as dirty, and writeback is delayed for as long as possible. + * Generally used to save registers before program buffer execution. + * + * TODO: The interface should be restricted in such a way that only GPRs can be + * saved. + */ +int riscv013_reg_save(struct target *target, enum gdb_regno regid); + +#endif /* OPENOCD_TARGET_RISCV_RISCV_013_REG_H */ diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 4ba0122ab..2b6d0c755 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -17,97 +17,27 @@ #include "jtag/jtag.h" #include "target/register.h" #include "target/breakpoints.h" +#include "helper/base64.h" +#include "helper/time_support.h" #include "riscv.h" +#include "riscv_reg.h" +#include "program.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))) - -/* Constants for legacy SiFive hardware breakpoints. */ -#define CSR_BPCONTROL_X (1<<0) -#define CSR_BPCONTROL_W (1<<1) -#define CSR_BPCONTROL_R (1<<2) -#define CSR_BPCONTROL_U (1<<3) -#define CSR_BPCONTROL_S (1<<4) -#define CSR_BPCONTROL_H (1<<5) -#define CSR_BPCONTROL_M (1<<6) -#define CSR_BPCONTROL_BPMATCH (0xf<<7) -#define CSR_BPCONTROL_BPACTION (0xff<<11) - -#define DEBUG_ROM_START 0x800 -#define DEBUG_ROM_RESUME (DEBUG_ROM_START + 4) -#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_START + 8) -#define DEBUG_RAM_START 0x400 - -#define SETHALTNOT 0x10c +#include "field_helpers.h" /*** JTAG registers. ***/ #define DTMCONTROL 0x10 -#define DTMCONTROL_DBUS_RESET (1<<16) -#define DTMCONTROL_IDLE (7<<10) -#define DTMCONTROL_ADDRBITS (0xf<<4) #define DTMCONTROL_VERSION (0xf) #define DBUS 0x11 -#define DBUS_OP_START 0 -#define DBUS_OP_SIZE 2 -typedef enum { - DBUS_OP_NOP = 0, - DBUS_OP_READ = 1, - DBUS_OP_WRITE = 2 -} dbus_op_t; -typedef enum { - DBUS_STATUS_SUCCESS = 0, - DBUS_STATUS_FAILED = 2, - DBUS_STATUS_BUSY = 3 -} dbus_status_t; -#define DBUS_DATA_START 2 -#define DBUS_DATA_SIZE 34 -#define DBUS_ADDRESS_START 36 -typedef enum slot { - SLOT0, - SLOT1, - SLOT_LAST, -} slot_t; +#define RISCV_TRIGGER_HIT_NOT_FOUND ((int64_t)-1) -/*** Debug Bus registers. ***/ - -#define DMCONTROL 0x10 -#define DMCONTROL_INTERRUPT (((uint64_t)1)<<33) -#define DMCONTROL_HALTNOT (((uint64_t)1)<<32) -#define DMCONTROL_BUSERROR (7<<19) -#define DMCONTROL_SERIAL (3<<16) -#define DMCONTROL_AUTOINCREMENT (1<<15) -#define DMCONTROL_ACCESS (7<<12) -#define DMCONTROL_HARTID (0x3ff<<2) -#define DMCONTROL_NDRESET (1<<1) -#define DMCONTROL_FULLRESET 1 - -#define DMINFO 0x11 -#define DMINFO_ABUSSIZE (0x7fU<<25) -#define DMINFO_SERIALCOUNT (0xf<<21) -#define DMINFO_ACCESS128 (1<<20) -#define DMINFO_ACCESS64 (1<<19) -#define DMINFO_ACCESS32 (1<<18) -#define DMINFO_ACCESS16 (1<<17) -#define DMINFO_ACCESS8 (1<<16) -#define DMINFO_DRAMSIZE (0x3f<<10) -#define DMINFO_AUTHENTICATED (1<<5) -#define DMINFO_AUTHBUSY (1<<4) -#define DMINFO_AUTHTYPE (3<<2) -#define DMINFO_VERSION 3 - -/*** Info about the core being debugged. ***/ - -#define DBUS_ADDRESS_UNKNOWN 0xffff - -#define MAX_HWBPS 16 -#define DRAM_CACHE_SIZE 16 +#define RISCV_HALT_GROUP_REPOLL_LIMIT 5 static uint8_t ir_dtmcontrol[4] = {DTMCONTROL}; struct scan_field select_dtmcontrol = { @@ -126,7 +56,9 @@ struct scan_field select_idcode = { }; static bscan_tunnel_type_t bscan_tunnel_type; -int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */ +#define BSCAN_TUNNEL_IR_WIDTH_NBITS 7 +uint8_t bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */ +static int bscan_tunnel_ir_id; /* IR ID of the JTAG TAP to access the tunnel. Valid when not 0 */ static const uint8_t bscan_zero[4] = {0}; static const uint8_t bscan_one[4] = {1}; @@ -138,7 +70,6 @@ static struct scan_field select_user4 = { }; -static uint8_t bscan_tunneled_ir_width[4] = {5}; /* overridden by assignment in riscv_init_target */ static struct scan_field _bscan_tunnel_data_register_select_dmi[] = { { .num_bits = 3, @@ -151,8 +82,8 @@ static struct scan_field _bscan_tunnel_data_register_select_dmi[] = { .in_value = NULL, }, { - .num_bits = 7, - .out_value = bscan_tunneled_ir_width, + .num_bits = BSCAN_TUNNEL_IR_WIDTH_NBITS, + .out_value = &bscan_tunnel_ir_width, .in_value = NULL, }, { @@ -169,8 +100,8 @@ static struct scan_field _bscan_tunnel_nested_tap_select_dmi[] = { .in_value = NULL, }, { - .num_bits = 7, - .out_value = bscan_tunneled_ir_width, + .num_bits = BSCAN_TUNNEL_IR_WIDTH_NBITS, + .out_value = &bscan_tunnel_ir_width, .in_value = NULL, }, { @@ -195,22 +126,60 @@ struct trigger { uint32_t length; uint64_t mask; uint64_t value; - bool read, write, execute; + bool is_read, is_write, is_execute; int unique_id; }; +struct tdata2_cache { + struct list_head elem_tdata2; + riscv_reg_t tdata2; +}; + +struct tdata1_cache { + riscv_reg_t tdata1; + struct list_head tdata2_cache_head; + struct list_head elem_tdata1; +}; + +bool riscv_virt2phys_mode_is_hw(const struct target *target) +{ + assert(target); + RISCV_INFO(r); + return r->virt2phys_mode == RISCV_VIRT2PHYS_MODE_HW; +} + +bool riscv_virt2phys_mode_is_sw(const struct target *target) +{ + assert(target); + RISCV_INFO(r); + return r->virt2phys_mode == RISCV_VIRT2PHYS_MODE_SW; +} + +const char *riscv_virt2phys_mode_to_str(enum riscv_virt2phys_mode mode) +{ + assert(mode == RISCV_VIRT2PHYS_MODE_OFF + || mode == RISCV_VIRT2PHYS_MODE_SW + || mode == RISCV_VIRT2PHYS_MODE_HW); + + static const char *const names[] = { + [RISCV_VIRT2PHYS_MODE_HW] = "hw", + [RISCV_VIRT2PHYS_MODE_SW] = "sw", + [RISCV_VIRT2PHYS_MODE_OFF] = "off", + }; + + return names[mode]; +} + /* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/ -int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC; +static int riscv_command_timeout_sec_value = DEFAULT_COMMAND_TIMEOUT_SEC; -/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ -int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC; +/* DEPRECATED Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ +static int riscv_reset_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC; -static bool riscv_enable_virt2phys = true; -bool riscv_ebreakm = true; -bool riscv_ebreaks = true; -bool riscv_ebreaku = true; - -bool riscv_enable_virtual; +int riscv_get_command_timeout_sec(void) +{ + return MAX(riscv_command_timeout_sec_value, riscv_reset_timeout_sec); +} static enum { RO_NORMAL, @@ -230,6 +199,19 @@ static const virt2phys_info_t sv32 = { .pa_ppn_mask = {0x3ff, 0xfff}, }; +static const virt2phys_info_t sv32x4 = { + .name = "Sv32x4", + .va_bits = 34, + .level = 2, + .pte_shift = 2, + .vpn_shift = {12, 22}, + .vpn_mask = {0x3ff, 0xfff}, + .pte_ppn_shift = {10, 20}, + .pte_ppn_mask = {0x3ff, 0xfff}, + .pa_ppn_shift = {12, 22}, + .pa_ppn_mask = {0x3ff, 0xfff}, +}; + static const virt2phys_info_t sv39 = { .name = "Sv39", .va_bits = 39, @@ -243,6 +225,19 @@ static const virt2phys_info_t sv39 = { .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff}, }; +static const virt2phys_info_t sv39x4 = { + .name = "Sv39x4", + .va_bits = 41, + .level = 3, + .pte_shift = 3, + .vpn_shift = {12, 21, 30}, + .vpn_mask = {0x1ff, 0x1ff, 0x7ff}, + .pte_ppn_shift = {10, 19, 28}, + .pte_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff}, + .pa_ppn_shift = {12, 21, 30}, + .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff}, +}; + static const virt2phys_info_t sv48 = { .name = "Sv48", .va_bits = 48, @@ -256,11 +251,53 @@ static const virt2phys_info_t sv48 = { .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff}, }; -static enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid); +static const virt2phys_info_t sv48x4 = { + .name = "Sv48x4", + .va_bits = 50, + .level = 4, + .pte_shift = 3, + .vpn_shift = {12, 21, 30, 39}, + .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x7ff}, + .pte_ppn_shift = {10, 19, 28, 37}, + .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff}, + .pa_ppn_shift = {12, 21, 30, 39}, + .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff}, +}; + +static const virt2phys_info_t sv57 = { + .name = "Sv57", + .va_bits = 57, + .level = 5, + .pte_shift = 3, + .vpn_shift = {12, 21, 30, 39, 48}, + .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0x1ff}, + .pte_ppn_shift = {10, 19, 28, 37, 46}, + .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff}, + .pa_ppn_shift = {12, 21, 30, 39, 48}, + .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff}, +}; + +static const virt2phys_info_t sv57x4 = { + .name = "Sv57x4", + .va_bits = 59, + .level = 5, + .pte_shift = 3, + .vpn_shift = {12, 21, 30, 39, 48}, + .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0x7ff}, + .pte_ppn_shift = {10, 19, 28, 37, 46}, + .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff}, + .pa_ppn_shift = {12, 21, 30, 39, 48}, + .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff}, +}; + +static enum riscv_halt_reason riscv_halt_reason(struct target *target); static void riscv_info_init(struct target *target, struct riscv_info *r); -static void riscv_invalidate_register_cache(struct target *target); static int riscv_step_rtos_hart(struct target *target); +static const riscv_reg_t mstatus_ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE; +static int riscv_interrupts_disable(struct target *target, riscv_reg_t *old_mstatus); +static int riscv_interrupts_restore(struct target *target, riscv_reg_t old_mstatus); + static void riscv_sample_buf_maybe_add_timestamp(struct target *target, bool before) { RISCV_INFO(r); @@ -279,21 +316,20 @@ static void riscv_sample_buf_maybe_add_timestamp(struct target *target, bool bef static int riscv_resume_go_all_harts(struct target *target); -void select_dmi_via_bscan(struct target *target) +void select_dmi_via_bscan(struct jtag_tap *tap) { - jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE); + jtag_add_ir_scan(tap, &select_user4, TAP_IDLE); if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) - jtag_add_dr_scan(target->tap, bscan_tunnel_data_register_select_dmi_num_fields, + jtag_add_dr_scan(tap, bscan_tunnel_data_register_select_dmi_num_fields, bscan_tunnel_data_register_select_dmi, TAP_IDLE); else /* BSCAN_TUNNEL_NESTED_TAP */ - jtag_add_dr_scan(target->tap, bscan_tunnel_nested_tap_select_dmi_num_fields, + jtag_add_dr_scan(tap, bscan_tunnel_nested_tap_select_dmi_num_fields, bscan_tunnel_nested_tap_select_dmi, TAP_IDLE); } -uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) +static int dtmcs_scan_via_bscan(struct jtag_tap *tap, uint32_t out, uint32_t *in_ptr) { /* On BSCAN TAP: Select IR=USER4, issue tunneled IR scan via BSCAN TAP's DR */ - uint8_t tunneled_ir_width[4] = {bscan_tunnel_ir_width}; uint8_t tunneled_dr_width[4] = {32}; uint8_t out_value[5] = {0}; uint8_t in_value[5] = {0}; @@ -309,8 +345,8 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) tunneled_ir[1].num_bits = bscan_tunnel_ir_width; tunneled_ir[1].out_value = ir_dtmcontrol; tunneled_ir[1].in_value = NULL; - tunneled_ir[2].num_bits = 7; - tunneled_ir[2].out_value = tunneled_ir_width; + tunneled_ir[2].num_bits = BSCAN_TUNNEL_IR_WIDTH_NBITS; + tunneled_ir[2].out_value = &bscan_tunnel_ir_width; tunneled_ir[2].in_value = NULL; tunneled_ir[3].num_bits = 1; tunneled_ir[3].out_value = bscan_zero; @@ -322,7 +358,7 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) tunneled_dr[1].num_bits = 32 + 1; tunneled_dr[1].out_value = out_value; tunneled_dr[1].in_value = in_value; - tunneled_dr[2].num_bits = 7; + tunneled_dr[2].num_bits = BSCAN_TUNNEL_IR_WIDTH_NBITS; tunneled_dr[2].out_value = tunneled_dr_width; tunneled_dr[2].in_value = NULL; tunneled_dr[3].num_bits = 1; @@ -336,8 +372,8 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) tunneled_ir[2].num_bits = bscan_tunnel_ir_width; tunneled_ir[2].out_value = ir_dtmcontrol; tunneled_ir[1].in_value = NULL; - tunneled_ir[1].num_bits = 7; - tunneled_ir[1].out_value = tunneled_ir_width; + tunneled_ir[1].num_bits = BSCAN_TUNNEL_IR_WIDTH_NBITS; + tunneled_ir[1].out_value = &bscan_tunnel_ir_width; tunneled_ir[2].in_value = NULL; tunneled_ir[0].num_bits = 1; tunneled_ir[0].out_value = bscan_zero; @@ -356,10 +392,10 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) tunneled_dr[0].out_value = bscan_one; tunneled_dr[0].in_value = NULL; } - jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE); - jtag_add_dr_scan(target->tap, ARRAY_SIZE(tunneled_ir), tunneled_ir, TAP_IDLE); - jtag_add_dr_scan(target->tap, ARRAY_SIZE(tunneled_dr), tunneled_dr, TAP_IDLE); - select_dmi_via_bscan(target); + jtag_add_ir_scan(tap, &select_user4, TAP_IDLE); + jtag_add_dr_scan(tap, ARRAY_SIZE(tunneled_ir), tunneled_ir, TAP_IDLE); + jtag_add_dr_scan(tap, ARRAY_SIZE(tunneled_dr), tunneled_dr, TAP_IDLE); + select_dmi_via_bscan(tap); int retval = jtag_execute_queue(); if (retval != ERROR_OK) { @@ -371,91 +407,280 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) uint32_t in = buf_get_u32(in_value, 1, 32); LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in); - return in; + if (in_ptr) + *in_ptr = in; + return ERROR_OK; } -static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) +/* TODO: rename "dtmcontrol"-> "dtmcs" */ +int dtmcs_scan(struct jtag_tap *tap, uint32_t out, uint32_t *in_ptr) { - struct scan_field field; - uint8_t in_value[4]; - uint8_t out_value[4] = { 0 }; + uint8_t value[4]; if (bscan_tunnel_ir_width != 0) - return dtmcontrol_scan_via_bscan(target, out); + return dtmcs_scan_via_bscan(tap, out, in_ptr); + buf_set_u32(value, 0, 32, out); - buf_set_u32(out_value, 0, 32, out); + jtag_add_ir_scan(tap, &select_dtmcontrol, TAP_IDLE); - jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE); - - field.num_bits = 32; - field.out_value = out_value; - field.in_value = in_value; - jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); + struct scan_field field = { + .num_bits = 32, + .out_value = value, + .in_value = in_ptr ? value : NULL + }; + jtag_add_dr_scan(tap, 1, &field, TAP_IDLE); /* Always return to dbus. */ - jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); + jtag_add_ir_scan(tap, &select_dbus, TAP_IDLE); int retval = jtag_execute_queue(); if (retval != ERROR_OK) { - LOG_ERROR("failed jtag scan: %d", retval); + LOG_ERROR("'dtmcs' scan failed on TAP %s, error code = %d", + jtag_tap_name(tap), retval); return retval; } - uint32_t in = buf_get_u32(field.in_value, 0, 32); - LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in); - - return in; + if (in_ptr) { + assert(field.in_value); + uint32_t in = buf_get_u32(field.in_value, 0, 32); + LOG_DEBUG("TAP %s: DTMCS: 0x%" PRIx32 " -> 0x%" PRIx32, + jtag_tap_name(tap), out, in); + *in_ptr = in; + } else { + LOG_DEBUG("TAP %s: DTMCS: 0x%" PRIx32 " -> ?", jtag_tap_name(tap), out); + } + return ERROR_OK; } static struct target_type *get_target_type(struct target *target) { if (!target->arch_info) { - LOG_ERROR("Target has not been initialized"); + LOG_TARGET_ERROR(target, "Target has not been initialized."); return NULL; } RISCV_INFO(info); switch (info->dtm_version) { - case 0: + case DTM_DTMCS_VERSION_0_11: return &riscv011_target; - case 1: + case DTM_DTMCS_VERSION_1_0: return &riscv013_target; default: - LOG_ERROR("Unsupported DTM version: %d", info->dtm_version); + /* TODO: once we have proper support for non-examined targets + * we should have an assert here */ + LOG_TARGET_ERROR(target, "Unsupported DTM version: %d", + info->dtm_version); return NULL; } } +static struct riscv_private_config *alloc_default_riscv_private_config(void) +{ + struct riscv_private_config * const config = malloc(sizeof(*config)); + if (!config) { + LOG_ERROR("Out of memory!"); + return NULL; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(config->dcsr_ebreak_fields); ++i) + config->dcsr_ebreak_fields[i] = true; + + return config; +} + static int riscv_create_target(struct target *target) { - LOG_DEBUG("riscv_create_target()"); + LOG_TARGET_DEBUG(target, "riscv_create_target()"); + struct riscv_private_config *config = target->private_config; + if (!config) { + config = alloc_default_riscv_private_config(); + if (!config) + return ERROR_FAIL; + target->private_config = config; + } target->arch_info = calloc(1, sizeof(struct riscv_info)); if (!target->arch_info) { - LOG_ERROR("Failed to allocate RISC-V target structure."); + LOG_TARGET_ERROR(target, "Failed to allocate RISC-V target structure."); return ERROR_FAIL; } riscv_info_init(target, target->arch_info); return ERROR_OK; } +static struct jim_nvp nvp_ebreak_config_opts[] = { + { .name = "m", .value = RISCV_MODE_M }, + { .name = "s", .value = RISCV_MODE_S }, + { .name = "u", .value = RISCV_MODE_U }, + { .name = "vs", .value = RISCV_MODE_VS }, + { .name = "vu", .value = RISCV_MODE_VU }, + { .name = NULL, .value = N_RISCV_MODE } +}; + +#define RISCV_EBREAK_MODE_INVALID -1 + +static struct jim_nvp nvp_ebreak_mode_opts[] = { + { .name = "exception", .value = false }, + { .name = "halt", .value = true }, + { .name = NULL, .value = RISCV_EBREAK_MODE_INVALID } +}; + +static int jim_configure_ebreak(struct riscv_private_config *config, struct jim_getopt_info *goi) +{ + if (goi->argc == 0) { + Jim_WrongNumArgs(goi->interp, 1, goi->argv - 1, + "[?execution_mode?] ?ebreak_action?"); + return JIM_ERR; + } + struct jim_nvp *common_mode_nvp; + if (jim_nvp_name2value_obj(goi->interp, nvp_ebreak_mode_opts, goi->argv[0], + &common_mode_nvp) == JIM_OK) { + /* Here a common "ebreak" action is processed, e.g: + * "riscv.cpu configure -ebreak halt" + */ + int res = jim_getopt_obj(goi, NULL); + if (res != JIM_OK) + return res; + for (int ebreak_ctl_i = 0; ebreak_ctl_i < N_RISCV_MODE; ++ebreak_ctl_i) + config->dcsr_ebreak_fields[ebreak_ctl_i] = common_mode_nvp->value; + return JIM_OK; + } + + /* Here a "ebreak" action for a specific execution mode is processed, e.g: + * "riscv.cpu configure -ebreak m halt" + */ + if (goi->argc < 2) { + Jim_WrongNumArgs(goi->interp, 2, goi->argv - 2, + "?ebreak_action?"); + return JIM_ERR; + } + struct jim_nvp *ctrl_nvp; + if (jim_getopt_nvp(goi, nvp_ebreak_config_opts, &ctrl_nvp) != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_ebreak_config_opts, /*hadprefix*/ true); + return JIM_ERR; + } + struct jim_nvp *mode_nvp; + if (jim_getopt_nvp(goi, nvp_ebreak_mode_opts, &mode_nvp) != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_ebreak_mode_opts, /*hadprefix*/ true); + return JIM_ERR; + } + config->dcsr_ebreak_fields[ctrl_nvp->value] = mode_nvp->value; + return JIM_OK; +} + +/** + * Obtain dcsr.ebreak* configuration as a Tcl dictionary. + * Print the resulting string to the "buffer" and return the string length. + * The "buffer" can be NULL, in which case only the length is computed but + * nothing is written. + */ +static int ebreak_config_to_tcl_dict(const struct riscv_private_config *config, + char *buffer) +{ + int len = 0; + const char *separator = ""; + for (int ebreak_ctl_i = 0; ebreak_ctl_i < N_RISCV_MODE; + ++ebreak_ctl_i) { + const char * const format = "%s%s %s"; + const char * const priv_mode = + jim_nvp_value2name_simple(nvp_ebreak_config_opts, ebreak_ctl_i)->name; + const char * const mode = jim_nvp_value2name_simple(nvp_ebreak_mode_opts, + config->dcsr_ebreak_fields[ebreak_ctl_i])->name; + if (!buffer) + len += snprintf(NULL, 0, format, separator, priv_mode, mode); + else + len += sprintf(buffer + len, format, separator, priv_mode, mode); + + separator = "\n"; + } + return len; +} + +static int jim_report_ebreak_config(const struct riscv_private_config *config, + Jim_Interp *interp) +{ + const int len = ebreak_config_to_tcl_dict(config, NULL); + char *str = malloc(len + 1); + if (!str) { + LOG_ERROR("Unable to allocate a string of %d bytes.", len + 1); + return JIM_ERR; + } + ebreak_config_to_tcl_dict(config, str); + Jim_SetResultString(interp, str, len); + free(str); + return JIM_OK; +} + +enum riscv_cfg_opts { + RISCV_CFG_EBREAK, + RISCV_CFG_INVALID = -1 +}; + +static struct jim_nvp nvp_config_opts[] = { + { .name = "-ebreak", .value = RISCV_CFG_EBREAK }, + { .name = NULL, .value = RISCV_CFG_INVALID } +}; + +static int riscv_jim_configure(struct target *target, + struct jim_getopt_info *goi) +{ + struct riscv_private_config *config = target->private_config; + if (!config) { + config = alloc_default_riscv_private_config(); + if (!config) + return JIM_ERR; + target->private_config = config; + } + if (!goi->argc) + return JIM_OK; + + struct jim_nvp *n; + int e = jim_nvp_name2value_obj(goi->interp, nvp_config_opts, + goi->argv[0], &n); + if (e != JIM_OK) + return JIM_CONTINUE; + + e = jim_getopt_obj(goi, NULL); + if (e != JIM_OK) + return e; + + if (!goi->is_configure && goi->argc > 0) { + /* Expecting no arguments */ + Jim_WrongNumArgs(goi->interp, 2, goi->argv - 2, ""); + return JIM_ERR; + } + switch (n->value) { + case RISCV_CFG_EBREAK: + return goi->is_configure + ? jim_configure_ebreak(config, goi) + : jim_report_ebreak_config(config, goi->interp); + default: + assert(false && "'jim_getopt_nvp' should have returned an error."); + } + return JIM_ERR; +} + static int riscv_init_target(struct command_context *cmd_ctx, struct target *target) { - LOG_DEBUG("riscv_init_target()"); + LOG_TARGET_DEBUG(target, "riscv_init_target()"); RISCV_INFO(info); info->cmd_ctx = cmd_ctx; + info->reset_delays_wait = -1; select_dtmcontrol.num_bits = target->tap->ir_length; select_dbus.num_bits = target->tap->ir_length; select_idcode.num_bits = target->tap->ir_length; if (bscan_tunnel_ir_width != 0) { - assert(target->tap->ir_length >= 6); - uint32_t ir_user4_raw = 0x23 << (target->tap->ir_length - 6); + uint32_t ir_user4_raw = bscan_tunnel_ir_id; + /* Provide a default value which target some Xilinx FPGA USER4 IR */ + if (ir_user4_raw == 0) { + assert(target->tap->ir_length >= 6); + ir_user4_raw = 0x23 << (target->tap->ir_length - 6); + } h_u32_to_le(ir_user4, ir_user4_raw); select_user4.num_bits = target->tap->ir_length; - bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width; if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) bscan_tunnel_data_register_select_dmi[1].num_bits = bscan_tunnel_ir_width; else /* BSCAN_TUNNEL_NESTED_TAP */ @@ -469,39 +694,56 @@ static int riscv_init_target(struct command_context *cmd_ctx, return ERROR_OK; } -static void riscv_free_registers(struct target *target) +static void free_wp_triggers_cache(struct target *target) { - /* Free the shared structure use for most registers. */ - if (target->reg_cache) { - if (target->reg_cache->reg_list) { - free(target->reg_cache->reg_list[0].arch_info); - /* Free the ones we allocated separately. */ - for (unsigned int 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); + RISCV_INFO(r); + + for (unsigned int i = 0; i < r->trigger_count; ++i) { + struct tdata1_cache *elem_1, *tmp_1; + list_for_each_entry_safe(elem_1, tmp_1, &r->wp_triggers_negative_cache[i], elem_tdata1) { + struct tdata2_cache *elem_2, *tmp_2; + list_for_each_entry_safe(elem_2, tmp_2, &elem_1->tdata2_cache_head, elem_tdata2) { + list_del(&elem_2->elem_tdata2); + free(elem_2); + } + list_del(&elem_1->elem_tdata1); + free(elem_1); } - free(target->reg_cache); } + free(r->wp_triggers_negative_cache); } static void riscv_deinit_target(struct target *target) { - LOG_DEBUG("riscv_deinit_target()"); + LOG_TARGET_DEBUG(target, "riscv_deinit_target()"); + + free(target->private_config); struct riscv_info *info = target->arch_info; struct target_type *tt = get_target_type(target); + if (!tt) + LOG_TARGET_ERROR(target, "Could not identify target type."); + + if (riscv_reg_flush_all(target) != ERROR_OK) + LOG_TARGET_ERROR(target, "Failed to flush registers. Ignoring this error."); if (tt && info && info->version_specific) tt->deinit_target(target); - riscv_free_registers(target); + riscv_reg_free_all(target); + free_wp_triggers_cache(target); if (!info) return; + free(info->reserved_triggers); + range_list_t *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &info->hide_csr, list) { + free(entry->name); + free(entry); + } + list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) { free(entry->name); free(entry); @@ -512,7 +754,6 @@ static void riscv_deinit_target(struct target *target) free(entry); } - free(info->reg_names); free(target->arch_info); target->arch_info = NULL; @@ -524,16 +765,170 @@ static void trigger_from_breakpoint(struct trigger *trigger, trigger->address = breakpoint->address; trigger->length = breakpoint->length; trigger->mask = ~0LL; - trigger->read = false; - trigger->write = false; - trigger->execute = true; + trigger->is_read = false; + trigger->is_write = false; + trigger->is_execute = true; /* unique_id is unique across both breakpoints and watchpoints. */ trigger->unique_id = breakpoint->unique_id; } -static int maybe_add_trigger_t1(struct target *target, - struct trigger *trigger, uint64_t tdata1) +static bool can_use_napot_match(struct trigger *trigger) { + riscv_reg_t addr = trigger->address; + riscv_reg_t size = trigger->length; + bool size_power_of_2 = (size & (size - 1)) == 0; + bool addr_aligned = (addr & (size - 1)) == 0; + return size > 1 && size_power_of_2 && addr_aligned; +} + +/* Find the next free trigger of the given type, without talking to the target. */ +static int find_next_free_trigger(struct target *target, int type, bool chained, + unsigned int *idx) +{ + assert(idx); + RISCV_INFO(r); + + unsigned int num_found = 0; + unsigned int num_required = chained ? 2 : 1; + + for (unsigned int i = *idx; i < r->trigger_count; i++) { + if (r->trigger_unique_id[i] == -1) { + if (r->trigger_tinfo[i] & (1 << type)) { + num_found++; + if (num_required == num_found) { + /* Found num_required consecutive free triggers - success, done. */ + *idx = i - (num_required - 1); + LOG_TARGET_DEBUG(target, + "%d trigger(s) of type %d found on index %u, " + "chained == %s", + num_required, type, *idx, + chained ? "true" : "false"); + return ERROR_OK; + } + /* Found a trigger but need more consecutive ones */ + continue; + } + } + /* Trigger already occupied or incompatible type. + * Reset the counter of found consecutive triggers */ + num_found = 0; + } + + return ERROR_FAIL; +} + +static int find_first_trigger_by_id(struct target *target, int unique_id) +{ + RISCV_INFO(r); + + for (unsigned int i = 0; i < r->trigger_count; i++) { + if (r->trigger_unique_id[i] == unique_id) + return i; + } + return -1; +} + +static unsigned int count_trailing_ones(riscv_reg_t reg) +{ + const unsigned int riscv_reg_bits = sizeof(riscv_reg_t) * CHAR_BIT; + for (unsigned int i = 0; i < riscv_reg_bits; i++) { + if ((1 & (reg >> i)) == 0) + return i; + } + return riscv_reg_bits; +} + +static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdata1, riscv_reg_t tdata2) +{ + RISCV_INFO(r); + assert(r->reserved_triggers); + assert(idx < r->trigger_count); + if (r->reserved_triggers[idx]) { + LOG_TARGET_DEBUG(target, + "Trigger %u is reserved by 'reserve_trigger' command.", idx); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + riscv_reg_t tdata1_rb, tdata2_rb; + // Select which trigger to use + if (riscv_reg_set(target, GDB_REGNO_TSELECT, idx) != ERROR_OK) + return ERROR_FAIL; + + // Disable the trigger by writing 0 to it + if (riscv_reg_set(target, GDB_REGNO_TDATA1, 0) != ERROR_OK) + return ERROR_FAIL; + + // Set trigger data for tdata2 (and tdata3 if it was supported) + if (riscv_reg_set(target, GDB_REGNO_TDATA2, tdata2) != ERROR_OK) + return ERROR_FAIL; + + // Set trigger data for tdata1 + if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1) != ERROR_OK) + return ERROR_FAIL; + + // Read back tdata1, tdata2, (tdata3), and check if the configuration is supported + if (riscv_reg_get(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK) + return ERROR_FAIL; + if (riscv_reg_get(target, &tdata2_rb, GDB_REGNO_TDATA2) != ERROR_OK) + return ERROR_FAIL; + + const uint32_t type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target))); + const bool is_mcontrol = type == CSR_TDATA1_TYPE_MCONTROL; + + /* Determine if tdata1 supports what we need. + * For mcontrol triggers, we don't care about + * the value in the read-only "maskmax" field. + */ + const riscv_reg_t tdata1_ignore_mask = is_mcontrol ? CSR_MCONTROL_MASKMAX(riscv_xlen(target)) : 0; + const bool tdata1_config_denied = (tdata1 & ~tdata1_ignore_mask) != (tdata1_rb & ~tdata1_ignore_mask); + + /* Determine if tdata1.maxmask is sufficient + * (only relevant for mcontrol triggers and NAPOT match type) + */ + bool unsupported_napot_range = false; + riscv_reg_t maskmax_value = 0; + if (!tdata1_config_denied) { + const bool is_napot_match = get_field(tdata1_rb, CSR_MCONTROL_MATCH) == CSR_MCONTROL_MATCH_NAPOT; + if (is_mcontrol && is_napot_match) { + maskmax_value = get_field(tdata1_rb, CSR_MCONTROL_MASKMAX(riscv_xlen(target))); + const unsigned int napot_size = count_trailing_ones(tdata2) + 1; + if (maskmax_value < napot_size) + unsupported_napot_range = true; + } + } + + const bool tdata2_config_denied = tdata2 != tdata2_rb; + if (tdata1_config_denied || tdata2_config_denied || unsupported_napot_range) { + LOG_TARGET_DEBUG(target, "Trigger %u doesn't support what we need.", idx); + + if (tdata1_config_denied) + LOG_TARGET_DEBUG(target, + "After writing 0x%" PRIx64 " to tdata1 it contains 0x%" PRIx64, + tdata1, tdata1_rb); + + if (tdata2_config_denied) + LOG_TARGET_DEBUG(target, + "After writing 0x%" PRIx64 " to tdata2 it contains 0x%" PRIx64, + tdata2, tdata2_rb); + + if (unsupported_napot_range) + LOG_TARGET_DEBUG(target, + "The requested NAPOT match range (tdata2=0x%" PRIx64 ") exceeds maskmax_value=0x%" PRIx64, + tdata2, maskmax_value); + + if (riscv_reg_set(target, GDB_REGNO_TDATA1, 0) != ERROR_OK) + return ERROR_FAIL; + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + return ERROR_OK; +} + +static int maybe_add_trigger_t1(struct target *target, struct trigger *trigger) +{ + int ret; + riscv_reg_t tdata1, tdata2; + RISCV_INFO(r); const uint32_t bpcontrol_x = 1<<0; @@ -546,202 +941,549 @@ static int maybe_add_trigger_t1(struct target *target, const uint32_t bpcontrol_bpmatch = 0xf << 7; const uint32_t bpcontrol_bpaction = 0xff << 11; + unsigned int idx = 0; + ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_LEGACY, false, &idx); + if (ret != ERROR_OK) + return ret; + + if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK) + return ERROR_FAIL; if (tdata1 & (bpcontrol_r | bpcontrol_w | bpcontrol_x)) { /* Trigger is already in use, presumably by user code. */ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - tdata1 = set_field(tdata1, bpcontrol_r, trigger->read); - tdata1 = set_field(tdata1, bpcontrol_w, trigger->write); - tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute); - tdata1 = set_field(tdata1, bpcontrol_u, - !!(r->misa & BIT('U' - 'A'))); - tdata1 = set_field(tdata1, bpcontrol_s, - !!(r->misa & BIT('S' - 'A'))); - tdata1 = set_field(tdata1, bpcontrol_h, - !!(r->misa & BIT('H' - 'A'))); - tdata1 |= bpcontrol_m; - tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */ + tdata1 = 0; + tdata1 = set_field(tdata1, bpcontrol_r, trigger->is_read); + tdata1 = set_field(tdata1, bpcontrol_w, trigger->is_write); + tdata1 = set_field(tdata1, bpcontrol_x, trigger->is_execute); + tdata1 = set_field(tdata1, bpcontrol_u, !!(r->misa & BIT('U' - 'A'))); + tdata1 = set_field(tdata1, bpcontrol_s, !!(r->misa & BIT('S' - 'A'))); + tdata1 = set_field(tdata1, bpcontrol_h, !!(r->misa & BIT('H' - 'A'))); + tdata1 = set_field(tdata1, bpcontrol_m, 1); tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */ - - riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); - - riscv_reg_t tdata1_rb; - if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK) - return ERROR_FAIL; - 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; - } - - riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); - + tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */ + tdata2 = trigger->address; + ret = set_trigger(target, idx, tdata1, tdata2); + if (ret != ERROR_OK) + return ret; + r->trigger_unique_id[idx] = trigger->unique_id; return ERROR_OK; } -static int maybe_add_trigger_t2(struct target *target, - struct trigger *trigger, uint64_t tdata1) +struct trigger_request_info { + riscv_reg_t tdata1; + riscv_reg_t tdata2; +}; + +static void log_trigger_request_info(struct trigger_request_info trig_info) +{ + LOG_DEBUG("tdata1=%" PRIx64 ", tdata2=%" PRIx64, trig_info.tdata1, trig_info.tdata2); +}; + +static struct tdata1_cache *tdata1_cache_alloc(struct list_head *tdata1_cache_head, riscv_reg_t tdata1) +{ + struct tdata1_cache *elem = (struct tdata1_cache *)calloc(1, sizeof(struct tdata1_cache)); + elem->tdata1 = tdata1; + INIT_LIST_HEAD(&elem->tdata2_cache_head); + list_add_tail(&elem->elem_tdata1, tdata1_cache_head); + return elem; +} + +static void tdata2_cache_alloc(struct list_head *tdata2_cache_head, riscv_reg_t tdata2) +{ + struct tdata2_cache * const elem = calloc(1, sizeof(struct tdata2_cache)); + elem->tdata2 = tdata2; + list_add(&elem->elem_tdata2, tdata2_cache_head); +} + +struct tdata2_cache *tdata2_cache_search(struct list_head *tdata2_cache_head, riscv_reg_t find_tdata2) +{ + struct tdata2_cache *elem_2; + list_for_each_entry(elem_2, tdata2_cache_head, elem_tdata2) { + if (elem_2->tdata2 == find_tdata2) + return elem_2; + } + return NULL; +} + +struct tdata1_cache *tdata1_cache_search(struct list_head *tdata1_cache_head, riscv_reg_t find_tdata1) +{ + struct tdata1_cache *elem_1; + list_for_each_entry(elem_1, tdata1_cache_head, elem_tdata1) { + if (elem_1->tdata1 == find_tdata1) + return elem_1; + } + return NULL; +} + +static void create_wp_trigger_cache(struct target *target) { RISCV_INFO(r); - /* tselect is already set */ - if (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD)) { - /* Trigger is already in use, presumably by user code. */ + r->wp_triggers_negative_cache = (struct list_head *)calloc(r->trigger_count, + sizeof(struct list_head)); + for (unsigned int i = 0; i < r->trigger_count; ++i) + INIT_LIST_HEAD(&r->wp_triggers_negative_cache[i]); +} + +static void wp_triggers_cache_add(struct target *target, unsigned int idx, riscv_reg_t tdata1, + riscv_reg_t tdata2, int error_code) +{ + RISCV_INFO(r); + + struct tdata1_cache *tdata1_cache = tdata1_cache_search(&r->wp_triggers_negative_cache[idx], tdata1); + if (!tdata1_cache) { + tdata1_cache = tdata1_cache_alloc(&r->wp_triggers_negative_cache[idx], tdata1); + } else { + struct tdata2_cache *tdata2_cache = tdata2_cache_search(&tdata1_cache->tdata2_cache_head, tdata2); + if (tdata2_cache) { + list_move(&tdata2_cache->elem_tdata2, &tdata1_cache->tdata2_cache_head); + return; + } + } + tdata2_cache_alloc(&tdata1_cache->tdata2_cache_head, tdata2); +} + +static bool wp_triggers_cache_search(struct target *target, unsigned int idx, + riscv_reg_t tdata1, riscv_reg_t tdata2) +{ + RISCV_INFO(r); + + struct tdata1_cache *tdata1_cache = tdata1_cache_search(&r->wp_triggers_negative_cache[idx], tdata1); + if (!tdata1_cache) + return false; + struct tdata2_cache *tdata2_cache = tdata2_cache_search(&tdata1_cache->tdata2_cache_head, tdata2); + if (!tdata2_cache) + return false; + assert(tdata1_cache->tdata1 == tdata1 && tdata2_cache->tdata2 == tdata2); + return true; +} + +static int try_use_trigger_and_cache_result(struct target *target, unsigned int idx, riscv_reg_t tdata1, + riscv_reg_t tdata2) +{ + if (wp_triggers_cache_search(target, idx, tdata1, tdata2)) return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + int ret = set_trigger(target, idx, tdata1, tdata2); + + /* Add these values to the cache to remember that they are not supported. */ + if (ret == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + wp_triggers_cache_add(target, idx, tdata1, tdata2, ret); + return ret; +} + +static int try_setup_single_match_trigger(struct target *target, + struct trigger *trigger, struct trigger_request_info trig_info) +{ + LOG_TARGET_DEBUG(target, "trying to set up a match trigger"); + log_trigger_request_info(trig_info); + + int trigger_type = + get_field(trig_info.tdata1, CSR_MCONTROL_TYPE(riscv_xlen(target))); + int ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + RISCV_INFO(r); + + /* Find the first trigger, supporting required tdata1 value */ + for (unsigned int idx = 0; + find_next_free_trigger(target, trigger_type, false, &idx) == ERROR_OK; + ++idx) { + ret = try_use_trigger_and_cache_result(target, idx, trig_info.tdata1, trig_info.tdata2); + + if (ret == ERROR_OK) { + r->trigger_unique_id[idx] = trigger->unique_id; + return ERROR_OK; + } + if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return ret; + } + return ret; +} + +static int try_setup_chained_match_triggers(struct target *target, + struct trigger *trigger, struct trigger_request_info t1, + struct trigger_request_info t2) +{ + LOG_TARGET_DEBUG(target, "trying to set up a chain of match triggers"); + log_trigger_request_info(t1); + log_trigger_request_info(t2); + int trigger_type = + get_field(t1.tdata1, CSR_MCONTROL_TYPE(riscv_xlen(target))); + int ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + RISCV_INFO(r); + + /* Find the first 2 consecutive triggers, supporting required tdata1 values */ + for (unsigned int idx = 0; + find_next_free_trigger(target, trigger_type, true, &idx) == ERROR_OK; + ++idx) { + ret = try_use_trigger_and_cache_result(target, idx, t1.tdata1, t1.tdata2); + + if (ret == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + continue; + else if (ret != ERROR_OK) + return ret; + + ret = try_use_trigger_and_cache_result(target, idx + 1, t2.tdata1, t2.tdata2); + + if (ret == ERROR_OK) { + r->trigger_unique_id[idx] = trigger->unique_id; + r->trigger_unique_id[idx + 1] = trigger->unique_id; + return ERROR_OK; + } + /* Undo the setting of the previous trigger */ + int ret_undo = set_trigger(target, idx, 0, 0); + if (ret_undo != ERROR_OK) + return ret_undo; + + if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return ret; + } + return ret; +} + +struct match_triggers_tdata1_fields { + riscv_reg_t common; + struct { + /* Other values are available for this field, + * but currently only `any` is needed. + */ + riscv_reg_t any; + } size; + struct { + riscv_reg_t enable; + riscv_reg_t disable; + } chain; + struct { + riscv_reg_t napot; + riscv_reg_t lt; + riscv_reg_t ge; + riscv_reg_t eq; + } match; +}; + +static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t2(struct target *target, + struct trigger *trigger) +{ + RISCV_INFO(r); + + struct match_triggers_tdata1_fields result = { + .common = + field_value(CSR_MCONTROL_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_MCONTROL) | + field_value(CSR_MCONTROL_DMODE(riscv_xlen(target)), 1) | + field_value(CSR_MCONTROL_ACTION, CSR_MCONTROL_ACTION_DEBUG_MODE) | + field_value(CSR_MCONTROL_M, 1) | + field_value(CSR_MCONTROL_S, !!(r->misa & BIT('S' - 'A'))) | + field_value(CSR_MCONTROL_U, !!(r->misa & BIT('U' - 'A'))) | + field_value(CSR_MCONTROL_EXECUTE, trigger->is_execute) | + field_value(CSR_MCONTROL_LOAD, trigger->is_read) | + field_value(CSR_MCONTROL_STORE, trigger->is_write), + .size = { + .any = + field_value(CSR_MCONTROL_SIZELO, CSR_MCONTROL_SIZELO_ANY & 3) | + field_value(CSR_MCONTROL_SIZEHI, (CSR_MCONTROL_SIZELO_ANY >> 2) & 3) + }, + .chain = { + .enable = field_value(CSR_MCONTROL_CHAIN, CSR_MCONTROL_CHAIN_ENABLED), + .disable = field_value(CSR_MCONTROL_CHAIN, CSR_MCONTROL_CHAIN_DISABLED) + }, + .match = { + .napot = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_NAPOT), + .lt = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_LT), + .ge = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_GE), + .eq = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_EQUAL) + } + }; + return result; +} + +static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t6(struct target *target, + struct trigger *trigger) +{ + bool misa_s = riscv_supports_extension(target, 'S'); + bool misa_u = riscv_supports_extension(target, 'U'); + bool misa_h = riscv_supports_extension(target, 'H'); + + struct match_triggers_tdata1_fields result = { + .common = + field_value(CSR_MCONTROL6_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_MCONTROL6) | + field_value(CSR_MCONTROL6_DMODE(riscv_xlen(target)), 1) | + field_value(CSR_MCONTROL6_ACTION, CSR_MCONTROL_ACTION_DEBUG_MODE) | + field_value(CSR_MCONTROL6_M, 1) | + field_value(CSR_MCONTROL6_S, misa_s) | + field_value(CSR_MCONTROL6_U, misa_u) | + field_value(CSR_MCONTROL6_VS, misa_h && misa_s) | + field_value(CSR_MCONTROL6_VU, misa_h && misa_u) | + field_value(CSR_MCONTROL6_EXECUTE, trigger->is_execute) | + field_value(CSR_MCONTROL6_LOAD, trigger->is_read) | + field_value(CSR_MCONTROL6_STORE, trigger->is_write), + .size = { + .any = field_value(CSR_MCONTROL6_SIZE, CSR_MCONTROL6_SIZE_ANY) + }, + .chain = { + .enable = field_value(CSR_MCONTROL6_CHAIN, CSR_MCONTROL6_CHAIN_ENABLED), + .disable = field_value(CSR_MCONTROL6_CHAIN, CSR_MCONTROL6_CHAIN_DISABLED) + }, + .match = { + .napot = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_NAPOT), + .lt = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_LT), + .ge = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_GE), + .eq = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_EQUAL) + } + }; + return result; +} + +static int maybe_add_trigger_t2_t6_for_wp(struct target *target, + struct trigger *trigger, struct match_triggers_tdata1_fields fields) +{ + RISCV_INFO(r); + int ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + if (trigger->length > 0) { + /* Setting a load/store trigger ("watchpoint") on a range of addresses */ + if (can_use_napot_match(trigger)) { + if (r->wp_allow_napot_trigger) { + LOG_TARGET_DEBUG(target, "trying to setup NAPOT match trigger"); + struct trigger_request_info napot = { + .tdata1 = fields.common | fields.size.any | + fields.chain.disable | fields.match.napot, + .tdata2 = trigger->address | ((trigger->length - 1) >> 1) + }; + ret = try_setup_single_match_trigger(target, trigger, napot); + if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return ret; + } else { + LOG_TARGET_DEBUG(target, "NAPOT match triggers are disabled for watchpoints. " + "Use 'riscv set_enable_trigger_feature napot wp' to enable it."); + } + } + + if (r->wp_allow_ge_lt_trigger) { + LOG_TARGET_DEBUG(target, "trying to setup GE+LT chained match trigger pair"); + struct trigger_request_info ge_1 = { + .tdata1 = fields.common | fields.size.any | fields.chain.enable | + fields.match.ge, + .tdata2 = trigger->address + }; + struct trigger_request_info lt_2 = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.lt, + .tdata2 = trigger->address + trigger->length + }; + ret = try_setup_chained_match_triggers(target, trigger, ge_1, lt_2); + if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return ret; + + LOG_TARGET_DEBUG(target, "trying to setup LT+GE chained match trigger pair"); + struct trigger_request_info lt_1 = { + .tdata1 = fields.common | fields.size.any | fields.chain.enable | + fields.match.lt, + .tdata2 = trigger->address + trigger->length + }; + struct trigger_request_info ge_2 = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.ge, + .tdata2 = trigger->address + }; + ret = try_setup_chained_match_triggers(target, trigger, lt_1, ge_2); + if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + return ret; + } else { + LOG_TARGET_DEBUG(target, "LT+GE chained match triggers are disabled for watchpoints. " + "Use 'riscv set_enable_trigger_feature ge_lt wp' to enable it."); + } } - /* address/data match trigger */ - tdata1 |= MCONTROL_DMODE(riscv_xlen(target)); - tdata1 = set_field(tdata1, MCONTROL_ACTION, - MCONTROL_ACTION_DEBUG_MODE); - tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL); - tdata1 |= MCONTROL_M; - if (r->misa & (1 << ('S' - 'A'))) - tdata1 |= MCONTROL_S; - if (r->misa & (1 << ('U' - 'A'))) - tdata1 |= MCONTROL_U; - - if (trigger->execute) - tdata1 |= MCONTROL_EXECUTE; - if (trigger->read) - tdata1 |= MCONTROL_LOAD; - if (trigger->write) - tdata1 |= MCONTROL_STORE; - - riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); - - 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; + if (r->wp_allow_equality_match_trigger) { + LOG_TARGET_DEBUG(target, "trying to setup equality match trigger"); + struct trigger_request_info eq = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.eq, + .tdata2 = trigger->address + }; + ret = try_setup_single_match_trigger(target, trigger, eq); + if (ret != ERROR_OK) + return ret; + } else { + LOG_TARGET_DEBUG(target, "equality match triggers are disabled for watchpoints. " + "Use 'riscv set_enable_trigger_feature eq wp' to enable it."); } - riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); + if (ret == ERROR_OK && trigger->length > 1) { + LOG_TARGET_DEBUG(target, "Trigger will match accesses at address 0x%" TARGET_PRIxADDR + ", but may not match accesses at addresses in the inclusive range from 0x%" + TARGET_PRIxADDR " to 0x%" TARGET_PRIxADDR ".", trigger->address, + trigger->address + 1, trigger->address + trigger->length - 1); + RISCV_INFO(info); + if (!info->range_trigger_fallback_encountered) + /* This message is displayed only once per target to avoid + * overwhelming the user with such messages on resume. + */ + LOG_TARGET_WARNING(target, + "Could not set a trigger that will match a whole address range. " + "As a fallback, this trigger (and maybe others) will only match " + "against the first address of the range."); + info->range_trigger_fallback_encountered = true; + } + return ret; +} + +static int maybe_add_trigger_t2_t6_for_bp(struct target *target, + struct trigger *trigger, struct match_triggers_tdata1_fields fields) +{ + LOG_TARGET_DEBUG(target, "trying to setup equality match trigger"); + struct trigger_request_info eq = { + .tdata1 = fields.common | fields.size.any | fields.chain.disable | + fields.match.eq, + .tdata2 = trigger->address + }; + + return try_setup_single_match_trigger(target, trigger, eq); +} + +static int maybe_add_trigger_t2_t6(struct target *target, + struct trigger *trigger, struct match_triggers_tdata1_fields fields) +{ + if (trigger->is_execute) { + assert(!trigger->is_read && !trigger->is_write); + return maybe_add_trigger_t2_t6_for_bp(target, trigger, fields); + } + + assert(trigger->is_read || trigger->is_write); + return maybe_add_trigger_t2_t6_for_wp(target, trigger, fields); +} + +static int maybe_add_trigger_t3(struct target *target, bool vs, bool vu, + bool m, bool s, bool u, bool pending, unsigned int count, + int unique_id) +{ + int ret; + riscv_reg_t tdata1; + + RISCV_INFO(r); + + tdata1 = 0; + tdata1 = set_field(tdata1, CSR_ICOUNT_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ICOUNT); + tdata1 = set_field(tdata1, CSR_ICOUNT_DMODE(riscv_xlen(target)), 1); + tdata1 = set_field(tdata1, CSR_ICOUNT_ACTION, CSR_ICOUNT_ACTION_DEBUG_MODE); + tdata1 = set_field(tdata1, CSR_ICOUNT_VS, vs); + tdata1 = set_field(tdata1, CSR_ICOUNT_VU, vu); + tdata1 = set_field(tdata1, CSR_ICOUNT_PENDING, pending); + tdata1 = set_field(tdata1, CSR_ICOUNT_M, m); + tdata1 = set_field(tdata1, CSR_ICOUNT_S, s); + tdata1 = set_field(tdata1, CSR_ICOUNT_U, u); + tdata1 = set_field(tdata1, CSR_ICOUNT_COUNT, count); + + unsigned int idx = 0; + ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_ICOUNT, false, &idx); + if (ret != ERROR_OK) + return ret; + ret = set_trigger(target, idx, tdata1, 0); + if (ret != ERROR_OK) + return ret; + r->trigger_unique_id[idx] = unique_id; return ERROR_OK; } -static int maybe_add_trigger_t6(struct target *target, - struct trigger *trigger, uint64_t tdata1) +static int maybe_add_trigger_t4(struct target *target, bool vs, bool vu, + bool nmi, bool m, bool s, bool u, riscv_reg_t interrupts, + int unique_id) { + int ret; + riscv_reg_t tdata1, tdata2; + RISCV_INFO(r); - /* 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; - } + tdata1 = 0; + tdata1 = set_field(tdata1, CSR_ITRIGGER_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ITRIGGER); + tdata1 = set_field(tdata1, CSR_ITRIGGER_DMODE(riscv_xlen(target)), 1); + tdata1 = set_field(tdata1, CSR_ITRIGGER_ACTION, CSR_ITRIGGER_ACTION_DEBUG_MODE); + tdata1 = set_field(tdata1, CSR_ITRIGGER_VS, vs); + tdata1 = set_field(tdata1, CSR_ITRIGGER_VU, vu); + tdata1 = set_field(tdata1, CSR_ITRIGGER_NMI, nmi); + tdata1 = set_field(tdata1, CSR_ITRIGGER_M, m); + tdata1 = set_field(tdata1, CSR_ITRIGGER_S, s); + tdata1 = set_field(tdata1, CSR_ITRIGGER_U, u); - /* 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; + tdata2 = interrupts; - if (trigger->execute) - tdata1 |= CSR_MCONTROL6_EXECUTE; - if (trigger->read) - tdata1 |= CSR_MCONTROL6_LOAD; - if (trigger->write) - tdata1 |= CSR_MCONTROL6_STORE; + unsigned int idx = 0; + ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_ITRIGGER, false, &idx); + if (ret != ERROR_OK) + return ret; + ret = set_trigger(target, idx, tdata1, tdata2); + if (ret != ERROR_OK) + return ret; + r->trigger_unique_id[idx] = unique_id; + return ERROR_OK; +} - riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); +static int maybe_add_trigger_t5(struct target *target, bool vs, bool vu, + bool m, bool s, bool u, riscv_reg_t exception_codes, + int unique_id) +{ + int ret; + riscv_reg_t tdata1, tdata2; - 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); + RISCV_INFO(r); - 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; - } + tdata1 = 0; + tdata1 = set_field(tdata1, CSR_ETRIGGER_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ETRIGGER); + tdata1 = set_field(tdata1, CSR_ETRIGGER_DMODE(riscv_xlen(target)), 1); + tdata1 = set_field(tdata1, CSR_ETRIGGER_ACTION, CSR_ETRIGGER_ACTION_DEBUG_MODE); + tdata1 = set_field(tdata1, CSR_ETRIGGER_VS, vs); + tdata1 = set_field(tdata1, CSR_ETRIGGER_VU, vu); + tdata1 = set_field(tdata1, CSR_ETRIGGER_M, m); + tdata1 = set_field(tdata1, CSR_ETRIGGER_S, s); + tdata1 = set_field(tdata1, CSR_ETRIGGER_U, u); - riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); + tdata2 = exception_codes; + unsigned int idx = 0; + ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_ETRIGGER, false, &idx); + if (ret != ERROR_OK) + return ret; + ret = set_trigger(target, idx, tdata1, tdata2); + if (ret != ERROR_OK) + return ret; + r->trigger_unique_id[idx] = unique_id; 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; - + int ret; riscv_reg_t tselect; - if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + + ret = riscv_enumerate_triggers(target); + if (ret != ERROR_OK) + return ret; + + ret = riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT); + if (ret != ERROR_OK) + return ret; + + do { + ret = maybe_add_trigger_t1(target, trigger); + if (ret == ERROR_OK) + break; + ret = maybe_add_trigger_t2_t6(target, trigger, + fill_match_triggers_tdata1_fields_t2(target, trigger)); + if (ret == ERROR_OK) + break; + ret = maybe_add_trigger_t2_t6(target, trigger, + fill_match_triggers_tdata1_fields_t6(target, trigger)); + if (ret == ERROR_OK) + break; + } while (0); + + if (riscv_reg_set(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK && + ret == ERROR_OK) return ERROR_FAIL; - unsigned int i; - for (i = 0; i < r->trigger_count; i++) { - if (r->trigger_unique_id[i] != -1) - continue; - - riscv_set_register(target, GDB_REGNO_TSELECT, i); - - uint64_t 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))); - - switch (type) { - case 1: - result = maybe_add_trigger_t1(target, trigger, tdata1); - break; - case 2: - result = maybe_add_trigger_t2(target, trigger, tdata1); - break; - case 6: - result = maybe_add_trigger_t6(target, trigger, tdata1); - break; - default: - LOG_DEBUG("trigger %d has unknown type %d", i, type); - continue; - } - - if (result != ERROR_OK) - continue; - - LOG_DEBUG("[%d] Using trigger %d (type %d) for bp %d", target->coreid, - i, type, trigger->unique_id); - r->trigger_unique_id[i] = trigger->unique_id; - break; - } - - riscv_set_register(target, GDB_REGNO_TSELECT, tselect); - - if (i >= r->trigger_count) { - LOG_ERROR("Couldn't find an available hardware trigger."); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - - return ERROR_OK; + return ret; } /** @@ -864,24 +1606,25 @@ int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_ static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) { - LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address); + LOG_TARGET_DEBUG(target, "@0x%" TARGET_PRIxADDR, breakpoint->address); assert(breakpoint); if (breakpoint->type == BKPT_SOFT) { /** @todo check RVC for size/alignment */ if (!(breakpoint->length == 4 || breakpoint->length == 2)) { - LOG_ERROR("Invalid breakpoint length %d", breakpoint->length); + LOG_TARGET_ERROR(target, "Invalid breakpoint length %d", breakpoint->length); return ERROR_FAIL; } if (0 != (breakpoint->address % 2)) { - LOG_ERROR("Invalid breakpoint alignment for address 0x%" TARGET_PRIxADDR, breakpoint->address); + LOG_TARGET_ERROR(target, "Invalid breakpoint alignment for address 0x%" TARGET_PRIxADDR, + breakpoint->address); return ERROR_FAIL; } /* Read the original instruction. */ if (riscv_read_by_any_size( target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { - LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR, + LOG_TARGET_ERROR(target, "Failed to read original instruction at 0x%" TARGET_PRIxADDR, breakpoint->address); return ERROR_FAIL; } @@ -890,10 +1633,11 @@ static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakp buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c()); /* Write the ebreak instruction. */ if (riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) { - LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%" + LOG_TARGET_ERROR(target, "Failed to write %d-byte breakpoint instruction at 0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); return ERROR_FAIL; } + breakpoint->is_set = true; } else if (breakpoint->type == BKPT_HARD) { struct trigger trigger; @@ -901,43 +1645,46 @@ static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakp int const result = add_trigger(target, &trigger); if (result != ERROR_OK) return result; + + int trigger_idx = find_first_trigger_by_id(target, breakpoint->unique_id); + breakpoint_hw_set(breakpoint, trigger_idx); } else { - LOG_INFO("OpenOCD only supports hardware and software breakpoints."); + LOG_TARGET_INFO(target, "OpenOCD only supports hardware and software breakpoints."); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - - breakpoint->is_set = true; return ERROR_OK; } -static int remove_trigger(struct target *target, struct trigger *trigger) +static int remove_trigger(struct target *target, int unique_id) { RISCV_INFO(r); if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; - unsigned int i; - for (i = 0; i < r->trigger_count; i++) { - if (r->trigger_unique_id[i] == trigger->unique_id) - break; - } - 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); - riscv_reg_t tselect; - int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT); + int result = riscv_reg_get(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; + + bool done = false; + for (unsigned int i = 0; i < r->trigger_count; i++) { + if (r->trigger_unique_id[i] == unique_id) { + riscv_reg_set(target, GDB_REGNO_TSELECT, i); + riscv_reg_set(target, GDB_REGNO_TDATA1, 0); + r->trigger_unique_id[i] = -1; + LOG_TARGET_DEBUG(target, "Stop using resource %d for bp %d", + i, unique_id); + done = true; + } + } + if (!done) { + LOG_TARGET_ERROR(target, + "Couldn't find the hardware resources used by hardware trigger."); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + riscv_reg_set(target, GDB_REGNO_TSELECT, tselect); return ERROR_OK; } @@ -949,7 +1696,7 @@ static int riscv_remove_breakpoint(struct target *target, /* 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 " + LOG_TARGET_ERROR(target, "Failed to restore instruction for %d-byte breakpoint at " "0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); return ERROR_FAIL; } @@ -957,12 +1704,12 @@ static int riscv_remove_breakpoint(struct target *target, } else if (breakpoint->type == BKPT_HARD) { struct trigger trigger; trigger_from_breakpoint(&trigger, breakpoint); - int result = remove_trigger(target, &trigger); + int result = remove_trigger(target, trigger.unique_id); if (result != ERROR_OK) return result; } else { - LOG_INFO("OpenOCD only supports hardware and software breakpoints."); + LOG_TARGET_INFO(target, "OpenOCD only supports hardware and software breakpoints."); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -978,22 +1725,29 @@ static void trigger_from_watchpoint(struct trigger *trigger, trigger->length = watchpoint->length; trigger->mask = watchpoint->mask; trigger->value = watchpoint->value; - trigger->read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS); - trigger->write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS); - trigger->execute = false; + trigger->is_read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS); + trigger->is_write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS); + trigger->is_execute = false; /* unique_id is unique across both breakpoints and watchpoints. */ trigger->unique_id = watchpoint->unique_id; } int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint) { + if (watchpoint->mask != WATCHPOINT_IGNORE_DATA_VALUE_MASK) { + LOG_TARGET_ERROR(target, "Watchpoints on data values are not implemented"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + struct trigger trigger; trigger_from_watchpoint(&trigger, watchpoint); int result = add_trigger(target, &trigger); if (result != ERROR_OK) return result; - watchpoint->is_set = true; + + int trigger_idx = find_first_trigger_by_id(target, watchpoint->unique_id); + watchpoint_set(watchpoint, trigger_idx); return ERROR_OK; } @@ -1001,12 +1755,12 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint) int riscv_remove_watchpoint(struct target *target, struct watchpoint *watchpoint) { - LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, watchpoint->address); + LOG_TARGET_DEBUG(target, "Removing watchpoint @0x%" TARGET_PRIxADDR, watchpoint->address); struct trigger trigger; trigger_from_watchpoint(&trigger, watchpoint); - int result = remove_trigger(target, &trigger); + int result = remove_trigger(target, trigger.unique_id); if (result != ERROR_OK) return result; watchpoint->is_set = false; @@ -1014,6 +1768,584 @@ int riscv_remove_watchpoint(struct target *target, return ERROR_OK; } +enum mctrl6hitstatus { + M6_HIT_ERROR, + M6_HIT_NOT_SUPPORTED, + M6_NOT_HIT, + M6_HIT_BEFORE, + M6_HIT_AFTER, + M6_HIT_IMM_AFTER +}; + +static enum mctrl6hitstatus check_mcontrol6_hit_status(struct target *target, + riscv_reg_t tdata1, uint64_t hit_mask) +{ + const uint32_t hit0 = get_field(tdata1, CSR_MCONTROL6_HIT0); + const uint32_t hit1 = get_field(tdata1, CSR_MCONTROL6_HIT1); + const uint32_t hit_info = (hit1 << 1) | hit0; + if (hit_info == CSR_MCONTROL6_HIT0_BEFORE) + return M6_HIT_BEFORE; + + if (hit_info == CSR_MCONTROL6_HIT0_AFTER) + return M6_HIT_AFTER; + + if (hit_info == CSR_MCONTROL6_HIT0_IMMEDIATELY_AFTER) + return M6_HIT_IMM_AFTER; + + if (hit_info == CSR_MCONTROL6_HIT0_FALSE) { + /* hit[1..0] equals 0, which can mean one of the following: + * - "hit" bits are supported and this trigger has not fired + * - "hit" bits are not supported on this trigger + * To distinguish these two cases, try writing all non-zero bit + * patterns to hit[1..0] to determine if the "hit" bits are supported: + */ + riscv_reg_t tdata1_tests[] = { + set_field(tdata1, CSR_MCONTROL6_HIT0, 1), + set_field(tdata1, CSR_MCONTROL6_HIT1, 1), + set_field(tdata1, CSR_MCONTROL6_HIT0, 1) | field_value(CSR_MCONTROL6_HIT1, 1) + }; + riscv_reg_t tdata1_test_rb; + for (uint64_t i = 0; i < ARRAY_SIZE(tdata1_tests); ++i) { + if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_tests[i]) != ERROR_OK) + return M6_HIT_ERROR; + if (riscv_reg_get(target, &tdata1_test_rb, GDB_REGNO_TDATA1) != ERROR_OK) + return M6_HIT_ERROR; + if (tdata1_test_rb == tdata1_tests[i]) { + if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1_test_rb & ~hit_mask) != ERROR_OK) + return M6_HIT_ERROR; + return M6_NOT_HIT; + } + } + } + return M6_HIT_NOT_SUPPORTED; +} + +/** + * Look at the trigger hit bits to find out which trigger is the reason we're + * halted. Sets *unique_id to the unique ID of that trigger. If *unique_id is + * RISCV_TRIGGER_HIT_NOT_FOUND, no match was found. + */ + +static int riscv_trigger_detect_hit_bits(struct target *target, int64_t *unique_id, + bool *need_single_step) +{ + /* FIXME: this function assumes that we have only one trigger that can + * have hit bit set. Debug spec allows hit bit to bit set if a trigger has + * matched but did not fire. Such targets will receive erroneous results. + */ + + RISCV_INFO(r); + assert(need_single_step); + *need_single_step = false; + + riscv_reg_t tselect; + if (riscv_reg_get(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; + + *unique_id = RISCV_TRIGGER_HIT_NOT_FOUND; + for (unsigned int i = 0; i < r->trigger_count; i++) { + if (r->trigger_unique_id[i] == -1) + continue; + + if (riscv_reg_set(target, GDB_REGNO_TSELECT, i) != ERROR_OK) + return ERROR_FAIL; + + uint64_t tdata1; + if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK) + return ERROR_FAIL; + int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target))); + + uint64_t hit_mask = 0; + switch (type) { + case CSR_TDATA1_TYPE_LEGACY: + /* Doesn't support hit bit. */ + break; + case CSR_TDATA1_TYPE_MCONTROL: + hit_mask = CSR_MCONTROL_HIT; + *need_single_step = true; + break; + case CSR_TDATA1_TYPE_MCONTROL6: + hit_mask = CSR_MCONTROL6_HIT0 | CSR_MCONTROL6_HIT1; + if (r->tinfo_version == CSR_TINFO_VERSION_0) { + *need_single_step = true; + } else if (r->tinfo_version == RISCV_TINFO_VERSION_UNKNOWN + || r->tinfo_version == CSR_TINFO_VERSION_1) { + enum mctrl6hitstatus hits_status = check_mcontrol6_hit_status(target, + tdata1, hit_mask); + if (hits_status == M6_HIT_ERROR) + return ERROR_FAIL; + if (hits_status == M6_HIT_BEFORE || hits_status == M6_HIT_NOT_SUPPORTED) + *need_single_step = true; + } + break; + case CSR_TDATA1_TYPE_ICOUNT: + hit_mask = CSR_ICOUNT_HIT; + break; + case CSR_TDATA1_TYPE_ITRIGGER: + hit_mask = CSR_ITRIGGER_HIT(riscv_xlen(target)); + break; + case CSR_TDATA1_TYPE_ETRIGGER: + hit_mask = CSR_ETRIGGER_HIT(riscv_xlen(target)); + break; + default: + LOG_TARGET_DEBUG(target, "Trigger %u has unknown type %d", i, type); + continue; + } + + /* FIXME: this logic needs to be changed to ignore triggers that are not + * the last one in the chain. */ + if (tdata1 & hit_mask) { + LOG_TARGET_DEBUG(target, "Trigger %u (unique_id=%" PRIi64 + ") has hit bit set. (need_single_step=%s)", + i, r->trigger_unique_id[i], (*need_single_step) ? "yes" : "no"); + if (riscv_reg_set(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK) + return ERROR_FAIL; + + *unique_id = r->trigger_unique_id[i]; + break; + } + } + + if (riscv_reg_set(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +/** + * These functions are needed to extract individual bits (for offset) + * from the instruction + */ +// c.lwsp rd_n0 c_uimm8sphi c_uimm8splo - offset[5] offset[4:2|7:6] +static uint16_t get_offset_clwsp(riscv_insn_t instruction) +{ + uint16_t offset_4to2and7to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM8SPLO); + uint16_t offset_4to2_bits = offset_4to2and7to6_bits >> 2; + uint16_t offset_7to6_bits = offset_4to2and7to6_bits & 0x3; + uint16_t offset_5_bit = get_field32(instruction, INSN_FIELD_C_UIMM8SPHI); + return (offset_4to2_bits << 2) + (offset_5_bit << 5) + + (offset_7to6_bits << 6); +} + +// c.ldsp rd_n0 c_uimm9sphi c_uimm9splo - offset[5] offset[4:3|8:6] +static uint16_t get_offset_cldsp(riscv_insn_t instruction) +{ + uint16_t offset_4to3and8to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM9SPLO); + uint16_t offset_4to3_bits = offset_4to3and8to6_bits >> 3; + uint16_t offset_8to6_bits = offset_4to3and8to6_bits & 0x7; + uint16_t offset_5_bit = get_field32(instruction, INSN_FIELD_C_UIMM9SPHI); + return (offset_4to3_bits << 3) + (offset_5_bit << 5) + + (offset_8to6_bits << 6); +} + +// c.swsp c_rs2 c_uimm8sp_s - offset[5:2|7:6] +static uint16_t get_offset_cswsp(riscv_insn_t instruction) +{ + uint16_t offset_5to2and7to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM8SP_S); + uint16_t offset_5to2_bits = offset_5to2and7to6_bits >> 2; + uint16_t offset_7to6_bits = offset_5to2and7to6_bits & 0x3; + return (offset_5to2_bits << 2) + (offset_7to6_bits << 6); +} + +// c.sdsp c_rs2 c_uimm9sp_s - offset[5:3|8:6] +static uint16_t get_offset_csdsp(riscv_insn_t instruction) +{ + uint16_t offset_5to3and8to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM9SP_S); + uint16_t offset_5to3_bits = offset_5to3and8to6_bits >> 3; + uint16_t offset_8to6_bits = offset_5to3and8to6_bits & 0x7; + return (offset_5to3_bits << 3) + (offset_8to6_bits << 6); +} + +// c.lw rd_p rs1_p c_uimm7lo c_uimm7hi - offset[2|6] offset[5:3] +static uint16_t get_offset_clw(riscv_insn_t instruction) +{ + uint16_t offset_2and6_bits = get_field32(instruction, INSN_FIELD_C_UIMM7LO); + uint16_t offset_2_bit = offset_2and6_bits >> 1; + uint16_t offset_6_bit = offset_2and6_bits & 0x1; + uint16_t offset_5to3_bits = get_field32(instruction, INSN_FIELD_C_UIMM7HI); + return (offset_2_bit << 2) + (offset_5to3_bits << 3) + (offset_6_bit << 6); +} + +// c.ld rd_p rs1_p c_uimm8lo c_uimm8hi - offset[7:6] offset[5:3] +static uint16_t get_offset_cld(riscv_insn_t instruction) +{ + uint16_t offset_7to6_bits = get_field32(instruction, INSN_FIELD_C_UIMM8LO); + uint16_t offset_5to3_bits = get_field32(instruction, INSN_FIELD_C_UIMM8HI); + return (offset_5to3_bits << 3) + (offset_7to6_bits << 6); +} + +// c.lq rd_p rs1_p c_uimm9lo c_uimm9hi - offset[7:6] offset[5|4|8] +static uint16_t get_offset_clq(riscv_insn_t instruction) +{ + uint16_t offset_7to6_bits = get_field32(instruction, INSN_FIELD_C_UIMM9LO); + uint16_t offset_5to4and8_bits = + get_field32(instruction, INSN_FIELD_C_UIMM9HI); + uint16_t offset_5to4_bits = offset_5to4and8_bits >> 1; + uint16_t offset_8_bit = offset_5to4and8_bits & 0x1; + return (offset_5to4_bits << 4) + (offset_7to6_bits << 6) + + (offset_8_bit << 8); +} + +// c.lqsp rd_n0 c_uimm10sphi c_uimm10splo - offset[5] offset[4|9:6] +static uint16_t get_offset_clqsp(riscv_insn_t instruction) +{ + uint16_t offset_4and9to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM10SPLO); + uint16_t offset_4_bit = offset_4and9to6_bits >> 4; + uint16_t offset_9to6_bits = offset_4and9to6_bits & 0xf; + uint16_t offset_5_bit = get_field32(instruction, INSN_FIELD_C_UIMM10SPHI); + return (offset_4_bit << 4) + (offset_5_bit << 5) + (offset_9to6_bits << 6); +} + +// c.sqsp c_rs2 c_uimm10sp_s - offset[5:4|9:6] +static uint16_t get_offset_csqsp(riscv_insn_t instruction) +{ + uint16_t offset_5to4and9to6_bits = + get_field32(instruction, INSN_FIELD_C_UIMM10SP_S); + uint16_t offset_5to4_biits = offset_5to4and9to6_bits >> 4; + uint16_t offset_9to6_bits = offset_5to4and9to6_bits & 0xf; + return (offset_5to4_biits << 4) + (offset_9to6_bits << 6); +} + +/** + * Decode rs1' register num for RVC. + * See "Table: Registers specified by the three-bit rs1′, rs2′, and rd′ fields + * of the CIW, CL, CS, CA, and CB formats" in "The RISC-V Instruction Set Manual + * Volume I: Unprivileged ISA". + * */ +static uint32_t get_rs1_c(riscv_insn_t instruction) +{ + return GDB_REGNO_S0 + get_field32(instruction, INSN_FIELD_C_SREG1); +} + +static uint32_t get_opcode(const riscv_insn_t instruction) +{ + // opcode is first 7 bits of the instruction + uint32_t opcode = instruction & INSN_FIELD_OPCODE; + if ((instruction & 0x03) < 0x03) { // opcode size RVC + // RVC MASK_C = 0xe003 for load/store instructions + opcode = instruction & MASK_C_LD; + } + return opcode; +} + +static int get_loadstore_membase_regno(struct target *target, + const riscv_insn_t instruction, int *regid) +{ + uint32_t opcode = get_opcode(instruction); + int rs; + + switch (opcode) { + case MATCH_LB: + case MATCH_FLH & ~INSN_FIELD_FUNCT3: + case MATCH_SB: + case MATCH_FSH & ~INSN_FIELD_FUNCT3: + rs = get_field32(instruction, INSN_FIELD_RS1); + break; + + case MATCH_C_LWSP: + case MATCH_C_LDSP: // if xlen >= 64 or MATCH_C_FLWSP: + case MATCH_C_FLDSP: // or MATCH_C_LQSP if xlen == 128 + case MATCH_C_SWSP: + case MATCH_C_SDSP: // if xlen >= 64 or MATCH_C_FSWSP: + case MATCH_C_FSDSP: // or MATCH_C_SQSP if xlen == 128 + rs = GDB_REGNO_SP; + break; + + case MATCH_C_LW: + case MATCH_C_FLW: // or MATCH_C_LD if xlen >= 64 + case MATCH_C_FLD: // or MATCH_C_LQ if xlen == 128 + case MATCH_C_SW: + case MATCH_C_FSW: // or MATCH_C_SD if xlen >= 64 + case MATCH_C_FSD: // or MATCH_C_SQ if xlen == 128 + rs = get_rs1_c(instruction); + break; + + default: + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is not a RV32I or \"C\" load or" + " store", instruction); + return ERROR_FAIL; + } + *regid = rs; + return ERROR_OK; +} + +static int get_loadstore_memoffset(struct target *target, + const riscv_insn_t instruction, int16_t *memoffset) +{ + uint32_t opcode = get_opcode(instruction); + int16_t offset = 0; + + switch (opcode) { + case MATCH_LB: + case MATCH_FLH & ~INSN_FIELD_FUNCT3: + case MATCH_SB: + case MATCH_FSH & ~INSN_FIELD_FUNCT3: + if (opcode == MATCH_SB || opcode == (MATCH_FSH & ~INSN_FIELD_FUNCT3)) { + offset = get_field32(instruction, INSN_FIELD_IMM12LO) | + (get_field32(instruction, INSN_FIELD_IMM12HI) << 5); + } else if (opcode == MATCH_LB || + opcode == (MATCH_FLH & ~INSN_FIELD_FUNCT3)) { + offset = get_field32(instruction, INSN_FIELD_IMM12); + } else { + assert(false); + } + /* sign extend 12-bit imm to 16-bits */ + if (offset & (1 << 11)) + offset |= 0xf000; + break; + + case MATCH_C_LWSP: + offset = get_offset_clwsp(instruction); + break; + + case MATCH_C_LDSP: // if xlen >= 64 or MATCH_C_FLWSP: + if (riscv_xlen(target) > 32) { // MATCH_C_LDSP + offset = get_offset_cldsp(instruction); + } else { // MATCH_C_FLWSP + offset = get_offset_clwsp(instruction); + } + break; + + case MATCH_C_FLDSP: // or MATCH_C_LQSP if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_LQSP + offset = get_offset_clqsp(instruction); + } else { // MATCH_C_FLDSP + offset = get_offset_cldsp(instruction); + } + break; + + case MATCH_C_SWSP: + offset = get_offset_cswsp(instruction); + break; + + case MATCH_C_SDSP: // if xlen >= 64 or MATCH_C_FSWSP: + if (riscv_xlen(target) > 32) { // MATCH_C_SDSP + offset = get_offset_csdsp(instruction); + } else { // MATCH_C_FSWSP + offset = get_offset_cswsp(instruction); + } + break; + + case MATCH_C_FSDSP: // or MATCH_C_SQSP if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_SQSP + offset = get_offset_csqsp(instruction); + } else { // MATCH_C_FSDSP + offset = get_offset_csdsp(instruction); // same as C.SDSP + } + break; + + case MATCH_C_LW: + offset = get_offset_clw(instruction); + break; + + case MATCH_C_FLW: // or MATCH_C_LD if xlen >= 64 + if (riscv_xlen(target) > 32) { // MATCH_C_LD + offset = get_offset_cld(instruction); + } else { // MATCH_C_FLW + offset = get_offset_clw(instruction); // same as C.FLW + } + break; + + case MATCH_C_FLD: // or MATCH_C_LQ if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_LQ + offset = get_offset_clq(instruction); + } else { // MATCH_C_FLD + offset = get_offset_cld(instruction); // same as C.LD + } + break; + + case MATCH_C_SW: + offset = get_offset_clw(instruction); // same as C.LW + break; + + case MATCH_C_FSW: // or MATCH_C_SD if xlen >= 64 + if (riscv_xlen(target) > 32) { // MATCH_C_SD + offset = get_offset_cld(instruction); // same as C.LD + } else { // MATCH_C_FSW + offset = get_offset_clw(instruction); // same as C.LW + } + break; + + case MATCH_C_FSD: // or MATCH_C_SQ if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_SQ + offset = get_offset_clq(instruction); // same as C.LQ + } else { // MATCH_C_FSD + offset = get_offset_cld(instruction); // same as C.LD + } + break; + + default: + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is not a RV32I or \"C\" load or" + " store", instruction); + return ERROR_FAIL; + } + *memoffset = offset; + return ERROR_OK; +} + +static int verify_loadstore(struct target *target, + const riscv_insn_t instruction, bool *is_read) +{ + uint32_t opcode = get_opcode(instruction); + bool misa_f = riscv_supports_extension(target, 'F'); + bool misa_d = riscv_supports_extension(target, 'D'); + enum watchpoint_rw rw; + + switch (opcode) { + case MATCH_LB: + case MATCH_FLH & ~INSN_FIELD_FUNCT3: + rw = WPT_READ; + break; + + case MATCH_SB: + case MATCH_FSH & ~INSN_FIELD_FUNCT3: + rw = WPT_WRITE; + break; + + case MATCH_C_LWSP: + if (get_field32(instruction, INSN_FIELD_RD) == 0) { + LOG_TARGET_DEBUG(target, + "The code points with rd = x0 are reserved for C.LWSP"); + return ERROR_FAIL; + } + rw = WPT_READ; + break; + + case MATCH_C_LDSP: // if xlen >= 64 or MATCH_C_FLWSP: + if (riscv_xlen(target) > 32) { // MATCH_C_LDSP + if (get_field32(instruction, INSN_FIELD_RD) == 0) { + LOG_TARGET_DEBUG(target, + "The code points with rd = x0 are reserved for C.LDSP"); + return ERROR_FAIL; + } + } else { // MATCH_C_FLWSP + if (!misa_f) { + LOG_TARGET_DEBUG(target, "Matched C.FLWSP but target doesn\'t " + "have the \"F\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_READ; + break; + + case MATCH_C_FLDSP: // or MATCH_C_LQSP if xlen == 128 + if (riscv_xlen(target) == 128) { // MATCH_C_LQSP + if (get_field32(instruction, INSN_FIELD_RD) == 0) { + LOG_TARGET_DEBUG(target, + "The code points with rd = x0 are reserved for C.LQSP"); + return ERROR_FAIL; + } + } else { // MATCH_C_FLDSP + if (!misa_d) { + LOG_TARGET_DEBUG(target, "Matched C.FLDSP but target doesn\'t " + "have the \"D\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_READ; + break; + + case MATCH_C_SWSP: + rw = WPT_WRITE; + break; + + case MATCH_C_SDSP: // if xlen >= 64 or MATCH_C_FSWSP: + if (riscv_xlen(target) == 32) { // MATCH_C_FSWSP + if (!misa_f) { + LOG_TARGET_DEBUG(target, "Matched C.FSWSP but target doesn\'t " + "have the \"F\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_WRITE; + break; + + case MATCH_C_FSDSP: // or MATCH_C_SQSP if xlen == 128 + if (riscv_xlen(target) != 128) { // MATCH_C_SQSP + if (!misa_d) { + LOG_TARGET_DEBUG(target, "Matched C.FSDSP but target doesn\'t " + "have the \"D\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_WRITE; + break; + + case MATCH_C_LW: + rw = WPT_READ; + break; + + case MATCH_C_FLW: // or MATCH_C_LD if xlen >= 64 + if (riscv_xlen(target) == 32) { // MATCH_C_FLW + if (!misa_f) { + LOG_TARGET_DEBUG(target, "Matched C.FLW but target doesn\'t " + "have the \"F\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_READ; + break; + + case MATCH_C_FLD: // or MATCH_C_LQ if xlen == 128 + if (riscv_xlen(target) != 128) { // MATCH_C_FLD + if (!misa_d) { + LOG_TARGET_DEBUG(target, "Matched C.FLD but target doesn\'t " + "have the \"D\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_READ; + break; + + case MATCH_C_SW: + rw = WPT_WRITE; + break; + + case MATCH_C_FSW: // or MATCH_C_SD if xlen >= 64 + if (riscv_xlen(target) == 32) { // MATCH_C_FSW + if (!misa_f) { + LOG_TARGET_DEBUG(target, "Matched C.FSW but target doesn\'t " + "have the \"F\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_WRITE; + break; + + case MATCH_C_FSD: // or MATCH_C_SQ if xlen == 128 + if (riscv_xlen(target) != 128) { // MATCH_C_FSD + if (!misa_d) { + LOG_TARGET_DEBUG(target, "Matched C.FSD but target doesn\'t " + "have the \"D\" extension"); + return ERROR_FAIL; + } + } + rw = WPT_WRITE; + break; + + default: + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is not a RV32I or \"C\" load or" + " store", instruction); + return ERROR_FAIL; + } + + if (rw == WPT_WRITE) { + *is_read = false; + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is store instruction", + instruction); + } else { + *is_read = true; + LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is load instruction", + instruction); + } + return ERROR_OK; +} + /* Sets *hit_watchpoint to the first watchpoint identified as causing the * current halt. * @@ -1022,67 +2354,74 @@ int riscv_remove_watchpoint(struct target *target, * and new value. */ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint) { - struct watchpoint *wp = target->watchpoints; + RISCV_INFO(r); - LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target)); + LOG_TARGET_DEBUG(target, "Hit Watchpoint"); + + /* If we identified which trigger caused the halt earlier, then just use + * that. */ + for (struct watchpoint *wp = target->watchpoints; wp; wp = wp->next) { + if (wp->unique_id == r->trigger_hit) { + *hit_watchpoint = wp; + return ERROR_OK; + } + } - /*TODO instead of disassembling the instruction that we think caused the - * trigger, check the hit bit of each watchpoint first. The hit bit is - * simpler and more reliable to check but as it is optional and relatively - * new, not all hardware will implement it */ riscv_reg_t dpc; - riscv_get_register(target, &dpc, GDB_REGNO_DPC); + if (riscv_reg_get(target, &dpc, GDB_REGNO_DPC) != ERROR_OK) + return ERROR_FAIL; const uint8_t length = 4; - LOG_DEBUG("dpc is 0x%" PRIx64, dpc); + LOG_TARGET_DEBUG(target, "dpc is 0x%" PRIx64, dpc); /* fetch the instruction at dpc */ uint8_t buffer[length]; if (target_read_buffer(target, dpc, length, buffer) != ERROR_OK) { - LOG_ERROR("Failed to read instruction at dpc 0x%" PRIx64, dpc); + LOG_TARGET_ERROR(target, "Failed to read instruction at dpc 0x%" PRIx64, + dpc); return ERROR_FAIL; } - uint32_t instruction = 0; + riscv_insn_t instruction = 0; for (int i = 0; i < length; i++) { - LOG_DEBUG("Next byte is %x", buffer[i]); + LOG_TARGET_DEBUG(target, "Next byte is %x", buffer[i]); instruction += (buffer[i] << 8 * i); } - LOG_DEBUG("Full instruction is %x", instruction); + LOG_TARGET_DEBUG(target, "Full instruction is %x", instruction); - /* find out which memory address is accessed by the instruction at dpc */ - /* opcode is first 7 bits of the instruction */ - uint8_t opcode = instruction & 0x7F; - uint32_t rs1; - int16_t imm; - riscv_reg_t mem_addr; + int rs; + target_addr_t mem_addr; + int16_t memoffset; - if (opcode == MATCH_LB || opcode == MATCH_SB) { - rs1 = (instruction & 0xf8000) >> 15; - riscv_get_register(target, &mem_addr, rs1); - - if (opcode == MATCH_SB) { - LOG_DEBUG("%x is store instruction", instruction); - imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20); - } else { - LOG_DEBUG("%x is load instruction", instruction); - imm = (instruction & 0xfff00000) >> 20; - } - /* sign extend 12-bit imm to 16-bits */ - if (imm & (1 << 11)) - imm |= 0xf000; - mem_addr += imm; - LOG_DEBUG("memory address=0x%" PRIx64, mem_addr); - } else { - LOG_DEBUG("%x is not a RV32I load or store", instruction); + if (get_loadstore_membase_regno(target, instruction, &rs) != ERROR_OK) + return ERROR_FAIL; + if (riscv_reg_get(target, &mem_addr, rs) != ERROR_OK) + return ERROR_FAIL; + if (get_loadstore_memoffset(target, instruction, &memoffset) != ERROR_OK) return ERROR_FAIL; - } + mem_addr += memoffset; + bool is_load; + + if (verify_loadstore(target, instruction, &is_load) != ERROR_OK) + return ERROR_FAIL; + + struct watchpoint *wp = target->watchpoints; while (wp) { - /*TODO support length/mask */ - if (wp->address == mem_addr) { + /* TODO support mask and check read/write/access */ + /* TODO check for intersection of the access range and watchpoint range + Recommended matching: + if (intersects(mem_addr, mem_addr + ref_size, wp->address, wp->address + wp->length)) + */ + if (mem_addr >= wp->address && + mem_addr < (wp->address + wp->length)) { *hit_watchpoint = wp; - LOG_DEBUG("Hit address=%" TARGET_PRIxADDR, wp->address); + LOG_TARGET_DEBUG(target, "WP hit found: %s 0x%" TARGET_PRIxADDR + " covered by %s wp at address 0x%" TARGET_PRIxADDR, + is_load ? "Load from" : "Store to", mem_addr, + (wp->rw == WPT_READ ? + "read" : (wp->rw == WPT_WRITE ? "write" : "access")), + wp->address); return ERROR_OK; } wp = wp->next; @@ -1093,6 +2432,8 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w * * OpenOCD will behave as if this function had never been implemented i.e. * report the halt to GDB with no address information. */ + LOG_TARGET_DEBUG(target, "No watchpoint found that would cover %s 0x%" + TARGET_PRIxADDR, is_load ? "load from" : "store to", mem_addr); return ERROR_FAIL; } @@ -1100,78 +2441,208 @@ static int oldriscv_step(struct target *target, bool current, uint32_t address, bool handle_breakpoints) { struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; return tt->step(target, current, address, handle_breakpoints); } +static int riscv_openocd_step_impl(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, int handle_callbacks); + +static int old_or_new_riscv_step_impl(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, int handle_callbacks) +{ + RISCV_INFO(r); + LOG_TARGET_DEBUG(target, "handle_breakpoints=%s", + handle_breakpoints ? "true" : "false"); + if (!r->get_hart_state) + return oldriscv_step(target, current, address, handle_breakpoints); + else + return riscv_openocd_step_impl(target, current, address, handle_breakpoints, + handle_callbacks); +} + static int old_or_new_riscv_step(struct target *target, bool current, target_addr_t address, bool handle_breakpoints) { - RISCV_INFO(r); - LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints); - if (!r->is_halted) - return oldriscv_step(target, current, address, handle_breakpoints); - else - return riscv_openocd_step(target, current, address, handle_breakpoints); + return old_or_new_riscv_step_impl(target, current, address, + handle_breakpoints, true /* handle callbacks*/); } static int riscv_examine(struct target *target) { - LOG_DEBUG("riscv_examine()"); + LOG_TARGET_DEBUG(target, "Starting examination"); if (target_was_examined(target)) { - LOG_DEBUG("Target was already examined."); + LOG_TARGET_DEBUG(target, "Target was already examined."); return ERROR_OK; } /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ RISCV_INFO(info); - uint32_t dtmcontrol = dtmcontrol_scan(target, 0); - LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); + uint32_t dtmcontrol; + if (dtmcs_scan(target->tap, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) { + LOG_TARGET_ERROR(target, "Could not read dtmcontrol. Check JTAG connectivity/board power."); + return ERROR_FAIL; + } + LOG_TARGET_DEBUG(target, "dtmcontrol=0x%x", dtmcontrol); info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION); - LOG_DEBUG(" version=0x%x", info->dtm_version); + LOG_TARGET_DEBUG(target, "version=0x%x", info->dtm_version); + int examine_status = ERROR_FAIL; struct target_type *tt = get_target_type(target); if (!tt) - return ERROR_FAIL; + goto examine_fail; - int result = tt->init_target(info->cmd_ctx, target); - if (result != ERROR_OK) - return result; + examine_status = tt->init_target(info->cmd_ctx, target); + if (examine_status != ERROR_OK) + goto examine_fail; - return tt->examine(target); + examine_status = tt->examine(target); + if (examine_status != ERROR_OK) + goto examine_fail; + + return ERROR_OK; + +examine_fail: + info->dtm_version = DTM_DTMCS_VERSION_UNKNOWN; + return examine_status; } static int oldriscv_poll(struct target *target) { struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; return tt->poll(target); } static int old_or_new_riscv_poll(struct target *target) { RISCV_INFO(r); - if (!r->is_halted) + if (!r->get_hart_state) return oldriscv_poll(target); else return riscv_openocd_poll(target); } -int riscv_select_current_hart(struct target *target) +static enum target_debug_reason +derive_debug_reason_without_hitbit(const struct target *target, riscv_reg_t dpc) { - return riscv_set_current_hartid(target, target->coreid); + /* TODO: if we detect that etrigger/itrigger/icount is set, we should + * just report DBG_REASON_UNKNOWN, since we can't disctiguish these + * triggers from BP/WP or from other triggers of such type. However, + * currently this renders existing testsuite as failing. We need to + * fix the testsuite first + */ + // TODO: the code below does not handle context-aware trigger types + for (const struct breakpoint *bp = target->breakpoints; bp; bp = bp->next) { + // TODO: investigate if we need to handle bp length + if (bp->type == BKPT_HARD && bp->is_set && bp->address == dpc) { + // FIXME: bp->linked_brp is uninitialized + if (bp->asid) { + LOG_TARGET_ERROR(target, + "can't derive debug reason for context-aware breakpoint: " + "unique_id = %" PRIu32 ", address = %" TARGET_PRIxADDR + ", asid = %" PRIx32 ", linked = %d", + bp->unique_id, bp->address, bp->asid, bp->linked_brp); + return DBG_REASON_UNDEFINED; + } + return DBG_REASON_BREAKPOINT; + } + } + return DBG_REASON_WATCHPOINT; +} +/** + * Set OpenOCD's generic debug reason from the RISC-V halt reason. + */ +static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason) +{ + RISCV_INFO(r); + r->trigger_hit = -1; + r->need_single_step = false; + switch (halt_reason) { + case RISCV_HALT_EBREAK: + target->debug_reason = DBG_REASON_BREAKPOINT; + break; + case RISCV_HALT_TRIGGER: + target->debug_reason = DBG_REASON_UNDEFINED; + if (riscv_trigger_detect_hit_bits(target, &r->trigger_hit, + &r->need_single_step) != ERROR_OK) + return ERROR_FAIL; + // FIXME: handle multiple hit bits + if (r->trigger_hit != RISCV_TRIGGER_HIT_NOT_FOUND) { + /* We scan for breakpoints first. If no breakpoints are found we still + * assume that debug reason is DBG_REASON_BREAKPOINT, unless + * there is a watchpoint match - This is to take + * ETrigger/ITrigger/ICount into account + */ + LOG_TARGET_DEBUG(target, + "Active hit bit is detected, trying to find trigger owner."); + for (struct breakpoint *bp = target->breakpoints; bp; bp = bp->next) { + if (bp->unique_id == r->trigger_hit) { + target->debug_reason = DBG_REASON_BREAKPOINT; + LOG_TARGET_DEBUG(target, + "Breakpoint with unique_id = %" PRIu32 " owns the trigger.", + bp->unique_id); + } + } + if (target->debug_reason == DBG_REASON_UNDEFINED) { + // by default we report all triggers as breakpoints + target->debug_reason = DBG_REASON_BREAKPOINT; + for (struct watchpoint *wp = target->watchpoints; wp; wp = wp->next) { + if (wp->unique_id == r->trigger_hit) { + target->debug_reason = DBG_REASON_WATCHPOINT; + LOG_TARGET_DEBUG(target, + "Watchpoint with unique_id = %" PRIu32 " owns the trigger.", + wp->unique_id); + } + } + } + } else { + LOG_TARGET_DEBUG(target, + "No trigger hit found, deriving debug reason without it."); + riscv_reg_t dpc; + if (riscv_reg_get(target, &dpc, GDB_REGNO_DPC) != ERROR_OK) + return ERROR_FAIL; + /* Here we don't have the hit bit set (likely, HW does not support it). + * We are trying to guess the state. But here comes the problem: + * if we have etrigger/itrigger/icount raised - we can't really + * distinguish it from the breakpoint or watchpoint. There is not + * much we can do here, except for checking current PC against pending + * breakpoints and hope for the best) + */ + target->debug_reason = derive_debug_reason_without_hitbit(target, dpc); + } + break; + case RISCV_HALT_INTERRUPT: + case RISCV_HALT_GROUP: + target->debug_reason = DBG_REASON_DBGRQ; + break; + case RISCV_HALT_SINGLESTEP: + target->debug_reason = DBG_REASON_SINGLESTEP; + break; + case RISCV_HALT_UNKNOWN: + target->debug_reason = DBG_REASON_UNDEFINED; + break; + case RISCV_HALT_ERROR: + return ERROR_FAIL; + } + LOG_TARGET_DEBUG(target, "debug_reason=%d", target->debug_reason); + + return ERROR_OK; } static int halt_prep(struct target *target) { RISCV_INFO(r); - LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target), - target->debug_reason); - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - if (riscv_is_halted(target)) { - LOG_DEBUG("[%s] Hart is already halted (reason=%d).", - target_name(target), target->debug_reason); + LOG_TARGET_DEBUG(target, "prep hart, debug_reason=%d", target->debug_reason); + r->prepped = false; + if (target->state == TARGET_HALTED) { + LOG_TARGET_DEBUG(target, "Hart is already halted."); + } else if (target->state == TARGET_UNAVAILABLE) { + LOG_TARGET_DEBUG(target, "Hart is unavailable."); } else { if (r->halt_prep(target) != ERROR_OK) return ERROR_FAIL; @@ -1185,17 +2656,29 @@ static int riscv_halt_go_all_harts(struct target *target) { RISCV_INFO(r); - if (riscv_select_current_hart(target) != ERROR_OK) + enum riscv_hart_state state; + if (riscv_get_hart_state(target, &state) != ERROR_OK) return ERROR_FAIL; - if (riscv_is_halted(target)) { - LOG_DEBUG("[%s] Hart is already halted.", target_name(target)); + if (state == RISCV_STATE_HALTED) { + LOG_TARGET_DEBUG(target, "Hart is already halted."); + if (target->state != TARGET_HALTED) { + target->state = TARGET_HALTED; + enum riscv_halt_reason halt_reason = riscv_halt_reason(target); + if (set_debug_reason(target, halt_reason) != ERROR_OK) + return ERROR_FAIL; + } } else { + // Safety check: + if (riscv_reg_cache_any_dirty(target, LOG_LVL_ERROR)) + LOG_TARGET_INFO(target, "BUG: Registers should not be dirty while " + "the target is not halted!"); + + riscv_reg_cache_invalidate_all(target); + if (r->halt_go(target) != ERROR_OK) return ERROR_FAIL; } - riscv_invalidate_register_cache(target); - return ERROR_OK; } @@ -1203,13 +2686,14 @@ static int halt_go(struct target *target) { RISCV_INFO(r); int result; - if (!r->is_halted) { + if (!r->get_hart_state) { struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; result = tt->halt(target); } else { result = riscv_halt_go_all_harts(target); } - target->state = TARGET_HALTED; if (target->debug_reason == DBG_REASON_NOTHALTED) target->debug_reason = DBG_REASON_DBGRQ; @@ -1225,12 +2709,14 @@ int riscv_halt(struct target *target) { RISCV_INFO(r); - if (!r->is_halted) { + if (!r->get_hart_state) { struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; return tt->halt(target); } - LOG_DEBUG("[%d] halting all harts", target->coreid); + LOG_TARGET_DEBUG(target, "halting all harts"); int result = ERROR_OK; if (target->smp) { @@ -1270,121 +2756,69 @@ int riscv_halt(struct target *target) static int riscv_assert_reset(struct target *target) { - LOG_DEBUG("[%d]", target->coreid); + LOG_TARGET_DEBUG(target, ""); struct target_type *tt = get_target_type(target); - riscv_invalidate_register_cache(target); + if (!tt) + return ERROR_FAIL; + + if (riscv_reg_cache_any_dirty(target, LOG_LVL_INFO)) + LOG_TARGET_INFO(target, "Discarding values of dirty registers."); + + riscv_reg_cache_invalidate_all(target); return tt->assert_reset(target); } static int riscv_deassert_reset(struct target *target) { - LOG_DEBUG("[%d]", target->coreid); + LOG_TARGET_DEBUG(target, ""); struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; return tt->deassert_reset(target); } -static int riscv_resume_prep_all_harts(struct target *target) +/* "wp_is_set" array must have at least "r->trigger_count" items. */ +static int disable_watchpoints(struct target *target, bool *wp_is_set) { RISCV_INFO(r); + LOG_TARGET_DEBUG(target, "Disabling triggers."); - LOG_DEBUG("[%s] prep hart", target_name(target)); - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - if (riscv_is_halted(target)) { - if (r->resume_prep(target) != ERROR_OK) - return ERROR_FAIL; - } else { - LOG_DEBUG("[%s] hart requested resume, but was already resumed", - target_name(target)); - } - - LOG_DEBUG("[%s] mark as prepped", target_name(target)); - r->prepped = true; - - return ERROR_OK; -} - -/* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */ -static int disable_triggers(struct target *target, riscv_reg_t *state) -{ - RISCV_INFO(r); - - LOG_DEBUG("deal with triggers"); - - if (riscv_enumerate_triggers(target) != ERROR_OK) - return ERROR_FAIL; - - if (r->manual_hwbp_set) { - /* Look at every trigger that may have been set. */ - riscv_reg_t tselect; - if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) - return ERROR_FAIL; - for (unsigned int t = 0; t < r->trigger_count; t++) { - if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) + /* TODO: The algorithm is flawed and may result in a situation described in + * https://github.com/riscv-collab/riscv-openocd/issues/1108 + */ + memset(wp_is_set, false, r->trigger_count); + struct watchpoint *watchpoint = target->watchpoints; + int i = 0; + while (watchpoint) { + LOG_TARGET_DEBUG(target, "Watchpoint %" PRIu32 ": set=%s", + watchpoint->unique_id, + wp_is_set[i] ? "true" : "false"); + wp_is_set[i] = watchpoint->is_set; + if (watchpoint->is_set) { + if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK) return ERROR_FAIL; - riscv_reg_t tdata1; - if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK) - return ERROR_FAIL; - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) { - state[t] = tdata1; - if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK) - return ERROR_FAIL; - } - } - if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK) - return ERROR_FAIL; - - } else { - /* Just go through the triggers we manage. */ - struct watchpoint *watchpoint = target->watchpoints; - int i = 0; - while (watchpoint) { - LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->is_set); - state[i] = watchpoint->is_set; - if (watchpoint->is_set) { - if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK) - return ERROR_FAIL; - } - watchpoint = watchpoint->next; - i++; } + watchpoint = watchpoint->next; + i++; } return ERROR_OK; } -static int enable_triggers(struct target *target, riscv_reg_t *state) +static int enable_watchpoints(struct target *target, bool *wp_is_set) { - RISCV_INFO(r); - - if (r->manual_hwbp_set) { - /* Look at every trigger that may have been set. */ - riscv_reg_t tselect; - if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) - return ERROR_FAIL; - for (unsigned int t = 0; t < r->trigger_count; t++) { - if (state[t] != 0) { - if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) - return ERROR_FAIL; - if (riscv_set_register(target, GDB_REGNO_TDATA1, state[t]) != ERROR_OK) - return ERROR_FAIL; - } - } - if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK) - return ERROR_FAIL; - - } else { - struct watchpoint *watchpoint = target->watchpoints; - int i = 0; - while (watchpoint) { - LOG_DEBUG("watchpoint %d: cleared=%" PRId64, i, state[i]); - if (state[i]) { - if (riscv_add_watchpoint(target, watchpoint) != ERROR_OK) - return ERROR_FAIL; - } - watchpoint = watchpoint->next; - i++; + struct watchpoint *watchpoint = target->watchpoints; + int i = 0; + while (watchpoint) { + LOG_TARGET_DEBUG(target, "Watchpoint %" PRIu32 + ": %s to be re-enabled.", watchpoint->unique_id, + wp_is_set[i] ? "needs " : "does not need"); + if (wp_is_set[i]) { + if (riscv_add_watchpoint(target, watchpoint) != ERROR_OK) + return ERROR_FAIL; } + watchpoint = watchpoint->next; + i++; } return ERROR_OK; @@ -1396,33 +2830,36 @@ static int enable_triggers(struct target *target, riscv_reg_t *state) static int resume_prep(struct target *target, bool current, target_addr_t address, bool handle_breakpoints, bool debug_execution) { + assert(target->state == TARGET_HALTED); RISCV_INFO(r); - LOG_DEBUG("[%d]", target->coreid); - if (!current) - riscv_set_register(target, GDB_REGNO_PC, address); + if (!current && riscv_reg_set(target, GDB_REGNO_PC, address) != ERROR_OK) + return ERROR_FAIL; - if (target->debug_reason == DBG_REASON_WATCHPOINT) { - /* To be able to run off a trigger, disable all the triggers, step, and - * then resume as usual. */ - riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0}; + if (handle_breakpoints) { + /* To be able to run off a trigger, we perform a step operation and then + * resume. If handle_breakpoints is true then step temporarily disables + * pending breakpoints so we can safely perform the step. + * + * Two cases where single step is needed before resuming: + * 1. ebreak used in software breakpoint; + * 2. a trigger that is taken just before the instruction that triggered it is retired. + */ + if (target->debug_reason == DBG_REASON_BREAKPOINT + || (target->debug_reason == DBG_REASON_WATCHPOINT + && r->need_single_step)) { + if (old_or_new_riscv_step_impl(target, current, address, handle_breakpoints, + false /* callbacks are not called */) != ERROR_OK) + return ERROR_FAIL; + } + } - if (disable_triggers(target, trigger_state) != ERROR_OK) - return ERROR_FAIL; - - if (old_or_new_riscv_step(target, true, 0, false) != ERROR_OK) - return ERROR_FAIL; - - if (enable_triggers(target, trigger_state) != ERROR_OK) + if (r->get_hart_state) { + if (r->resume_prep(target) != ERROR_OK) return ERROR_FAIL; } - if (r->is_halted) { - if (riscv_resume_prep_all_harts(target) != ERROR_OK) - return ERROR_FAIL; - } - - LOG_DEBUG("[%d] mark as prepped", target->coreid); + LOG_TARGET_DEBUG(target, "Mark as prepped."); r->prepped = true; return ERROR_OK; @@ -1435,10 +2872,13 @@ static int resume_prep(struct target *target, bool current, static int resume_go(struct target *target, bool current, target_addr_t address, bool handle_breakpoints, bool debug_execution) { + assert(target->state == TARGET_HALTED); RISCV_INFO(r); int result; - if (!r->is_halted) { + if (!r->get_hart_state) { struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; result = tt->resume(target, current, address, handle_breakpoints, debug_execution); } else { @@ -1448,66 +2888,89 @@ static int resume_go(struct target *target, bool current, return result; } -static int resume_finish(struct target *target) +static int resume_finish(struct target *target, bool debug_execution) { - register_cache_invalidate(target->reg_cache); + assert(target->state == TARGET_HALTED); + if (riscv_reg_cache_any_dirty(target, LOG_LVL_ERROR)) { + /* If this happens, it means there is a bug in the previous + * register-flushing algorithm: not all registers were flushed + * back to the target in preparation for the resume.*/ + LOG_TARGET_ERROR(target, + "BUG: registers should have been flushed by this point."); + } - target->state = TARGET_RUNNING; + riscv_reg_cache_invalidate_all(target); + + target->state = debug_execution ? TARGET_DEBUG_RUNNING : TARGET_RUNNING; target->debug_reason = DBG_REASON_NOTHALTED; - return target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + return target_call_event_callbacks(target, + debug_execution ? TARGET_EVENT_DEBUG_RESUMED : TARGET_EVENT_RESUMED); } /** * @par single_hart When true, only resume a single hart even if SMP is * configured. This is used to run algorithms on just one hart. */ -static int riscv_resume( - struct target *target, +static int riscv_resume(struct target *target, bool current, target_addr_t address, bool handle_breakpoints, bool debug_execution, bool single_hart) { - LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints); int result = ERROR_OK; + + struct list_head *targets; + + OOCD_LIST_HEAD(single_target_list); + struct target_list single_target_entry = { + .lh = {NULL, NULL}, + .target = target + }; + if (target->smp && !single_hart) { - 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, + targets = target->smp_targets; + } else { + /* Make a list that just contains a single target, so we can + * share code below. */ + list_add(&single_target_entry.lh, &single_target_list); + targets = &single_target_list; + } + + LOG_TARGET_DEBUG(target, "current=%s, address=0x%" + TARGET_PRIxADDR ", handle_breakpoints=%s, debug_exec=%s", + current ? "true" : "false", + address, + handle_breakpoints ? "true" : "false", + debug_execution ? "true" : "false"); + + struct target_list *tlist; + foreach_smp_target_direction(resume_order == RO_NORMAL, tlist, targets) { + struct target *t = tlist->target; + LOG_TARGET_DEBUG(t, "target->state=%s", target_state_name(t)); + if (t->state != TARGET_HALTED) + LOG_TARGET_DEBUG(t, "skipping this target: target not halted"); + else if (resume_prep(t, current, address, handle_breakpoints, + debug_execution) != ERROR_OK) + result = ERROR_FAIL; + } + + foreach_smp_target_direction(resume_order == RO_NORMAL, tlist, targets) { + struct target *t = tlist->target; + struct riscv_info *i = riscv_info(t); + if (i->prepped) { + if (resume_go(t, current, address, handle_breakpoints, debug_execution) != ERROR_OK) result = ERROR_FAIL; } + } - foreach_smp_target_direction(resume_order == RO_NORMAL, - tlist, target->smp_targets) { - struct target *t = tlist->target; - struct riscv_info *i = riscv_info(t); - if (i->prepped) { - if (resume_go(t, current, address, handle_breakpoints, - debug_execution) != ERROR_OK) - result = ERROR_FAIL; - } + foreach_smp_target_direction(resume_order == RO_NORMAL, tlist, targets) { + struct target *t = tlist->target; + if (t->state == TARGET_HALTED) { + if (resume_finish(t, debug_execution) != ERROR_OK) + result = ERROR_FAIL; } - - 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; - } - - } else { - if (resume_prep(target, current, address, handle_breakpoints, - debug_execution) != ERROR_OK) - result = ERROR_FAIL; - if (resume_go(target, current, address, handle_breakpoints, - debug_execution) != ERROR_OK) - result = ERROR_FAIL; - if (resume_finish(target) != ERROR_OK) - return ERROR_FAIL; } return result; @@ -1516,116 +2979,178 @@ static int riscv_resume( static int riscv_target_resume(struct target *target, bool current, target_addr_t address, bool handle_breakpoints, bool debug_execution) { + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Not halted."); + return ERROR_TARGET_NOT_HALTED; + } return riscv_resume(target, current, address, handle_breakpoints, debug_execution, false); } +static int riscv_effective_privilege_mode(struct target *target, int *v_mode, int *effective_mode) +{ + riscv_reg_t priv; + if (riscv_reg_get(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read priv register."); + return ERROR_FAIL; + } + *v_mode = get_field(priv, VIRT_PRIV_V); + + riscv_reg_t mstatus; + if (riscv_reg_get(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read mstatus register."); + return ERROR_FAIL; + } + + if (get_field(mstatus, MSTATUS_MPRV)) + *effective_mode = get_field(mstatus, MSTATUS_MPP); + else + *effective_mode = get_field(priv, VIRT_PRIV_PRV); + + LOG_TARGET_DEBUG(target, "Effective mode=%d; v=%d", *effective_mode, *v_mode); + + return ERROR_OK; +} + static int riscv_mmu(struct target *target, bool *enabled) { - if (!riscv_enable_virt2phys) { - *enabled = false; + *enabled = false; + + if (!riscv_virt2phys_mode_is_sw(target)) + return ERROR_OK; + + /* Don't use MMU in explicit or effective M (machine) mode */ + riscv_reg_t priv; + if (riscv_reg_get(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read priv register."); + return ERROR_FAIL; + } + + int effective_mode; + int v_mode; + if (riscv_effective_privilege_mode(target, &v_mode, &effective_mode) != ERROR_OK) + return ERROR_FAIL; + + unsigned int xlen = riscv_xlen(target); + + if (v_mode) { + /* vsatp and hgatp registers are considered active for the + * purposes of the address-translation algorithm unless the + * effective privilege mode is U and hstatus.HU=0. */ + if (effective_mode == PRV_U) { + riscv_reg_t hstatus; + if (riscv_reg_get(target, &hstatus, GDB_REGNO_HSTATUS) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read hstatus register."); + return ERROR_FAIL; + } + + if (get_field(hstatus, HSTATUS_HU) == 0) + /* In hypervisor mode regular satp translation + * doesn't happen. */ + return ERROR_OK; + } + + riscv_reg_t vsatp; + if (riscv_reg_get(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read vsatp register; priv=0x%" PRIx64, + priv); + return ERROR_FAIL; + } + /* vsatp is identical to satp, so we can use the satp macros. */ + if (RISCV_SATP_MODE(xlen) != SATP_MODE_OFF) { + LOG_TARGET_DEBUG(target, "VS-stage translation is enabled."); + *enabled = true; + return ERROR_OK; + } + + riscv_reg_t hgatp; + if (riscv_reg_get(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read hgatp register; priv=0x%" PRIx64, + priv); + return ERROR_FAIL; + } + if (RISCV_HGATP_MODE(xlen) != HGATP_MODE_OFF) { + LOG_TARGET_DEBUG(target, "G-stage address translation is enabled."); + *enabled = true; + } else { + LOG_TARGET_DEBUG(target, "No V-mode address translation enabled."); + } + return ERROR_OK; } /* Don't use MMU in explicit or effective M (machine) mode */ - riscv_reg_t priv; - if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) { - LOG_ERROR("Failed to read priv register."); - return ERROR_FAIL; - } - - riscv_reg_t mstatus; - if (riscv_get_register(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) { - LOG_ERROR("Failed to read mstatus register."); - return ERROR_FAIL; - } - - if ((get_field(mstatus, MSTATUS_MPRV) ? get_field(mstatus, MSTATUS_MPP) : priv) == PRV_M) { - LOG_DEBUG("SATP/MMU ignored in Machine mode (mstatus=0x%" PRIx64 ").", mstatus); - *enabled = false; + if (effective_mode == PRV_M) { + LOG_TARGET_DEBUG(target, "SATP/MMU ignored in Machine mode."); return ERROR_OK; } riscv_reg_t satp; - if (riscv_get_register(target, &satp, GDB_REGNO_SATP) != ERROR_OK) { - LOG_DEBUG("Couldn't read SATP."); + if (riscv_reg_get(target, &satp, GDB_REGNO_SATP) != ERROR_OK) { + LOG_TARGET_DEBUG(target, "Couldn't read SATP."); /* If we can't read SATP, then there must not be an MMU. */ - *enabled = false; return ERROR_OK; } - if (get_field(satp, RISCV_SATP_MODE(riscv_xlen(target))) == SATP_MODE_OFF) { - LOG_DEBUG("MMU is disabled."); - *enabled = false; + if (get_field(satp, RISCV_SATP_MODE(xlen)) == SATP_MODE_OFF) { + LOG_TARGET_DEBUG(target, "MMU is disabled."); } else { - LOG_DEBUG("MMU is enabled."); + LOG_TARGET_DEBUG(target, "MMU is enabled."); *enabled = true; } return ERROR_OK; } +/* Translate address from virtual to physical, using info and ppn. + * If extra_info is non-NULL, then translate page table accesses for the primary + * translation using extra_info and extra_ppn. */ static int riscv_address_translate(struct target *target, + const virt2phys_info_t *info, target_addr_t ppn, + const virt2phys_info_t *extra_info, target_addr_t extra_ppn, target_addr_t virtual, target_addr_t *physical) { RISCV_INFO(r); - riscv_reg_t satp_value; - int mode; - uint64_t ppn_value; - target_addr_t table_address; - const virt2phys_info_t *info; - uint64_t pte = 0; - int i; - - int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP); - if (result != ERROR_OK) - return result; - unsigned int xlen = riscv_xlen(target); - mode = get_field(satp_value, RISCV_SATP_MODE(xlen)); - switch (mode) { - case SATP_MODE_SV32: - info = &sv32; - break; - case SATP_MODE_SV39: - info = &sv39; - break; - case SATP_MODE_SV48: - info = &sv48; - break; - case SATP_MODE_OFF: - LOG_ERROR("No translation or protection." \ - " (satp: 0x%" PRIx64 ")", satp_value); - return ERROR_FAIL; - default: - LOG_ERROR("The translation mode is not supported." \ - " (satp: 0x%" PRIx64 ")", satp_value); - return ERROR_FAIL; - } - LOG_DEBUG("virtual=0x%" TARGET_PRIxADDR "; mode=%s", virtual, info->name); + + LOG_TARGET_DEBUG(target, "mode=%s; ppn=0x%" TARGET_PRIxADDR "; virtual=0x%" TARGET_PRIxADDR, + info->name, ppn, virtual); /* verify bits xlen-1:va_bits-1 are all equal */ assert(xlen >= info->va_bits); target_addr_t mask = ((target_addr_t)1 << (xlen - (info->va_bits - 1))) - 1; target_addr_t masked_msbs = (virtual >> (info->va_bits - 1)) & mask; if (masked_msbs != 0 && masked_msbs != mask) { - LOG_ERROR("Virtual address 0x%" TARGET_PRIxADDR " is not sign-extended " + LOG_TARGET_ERROR(target, "Virtual address 0x%" TARGET_PRIxADDR " is not sign-extended " "for %s mode.", virtual, info->name); return ERROR_FAIL; } - ppn_value = get_field(satp_value, RISCV_SATP_PPN(xlen)); - table_address = ppn_value << RISCV_PGSHIFT; - i = info->level - 1; + uint64_t pte = 0; + target_addr_t table_address = ppn << RISCV_PGSHIFT; + int i = info->level - 1; while (i >= 0) { uint64_t vpn = virtual >> info->vpn_shift[i]; vpn &= info->vpn_mask[i]; - target_addr_t pte_address = table_address + - (vpn << info->pte_shift); + target_addr_t pte_address = table_address + (vpn << info->pte_shift); + + if (extra_info) { + /* Perform extra stage translation. */ + if (riscv_address_translate(target, extra_info, extra_ppn, + NULL, 0, pte_address, &pte_address) != ERROR_OK) + return ERROR_FAIL; + } + uint8_t buffer[8]; assert(info->pte_shift <= 3); - int retval = r->read_memory(target, pte_address, - 4, (1 << info->pte_shift) / 4, buffer, 4); + const struct riscv_mem_access_args args = { + .address = pte_address, + .read_buffer = buffer, + .size = 4, + .increment = 4, + .count = (1 << info->pte_shift) / 4, + }; + int retval = r->access_memory(target, args); if (retval != ERROR_OK) return ERROR_FAIL; @@ -1634,24 +3159,27 @@ static int riscv_address_translate(struct target *target, else pte = buf_get_u64(buffer, 0, 64); - LOG_DEBUG("i=%d; PTE @0x%" TARGET_PRIxADDR " = 0x%" PRIx64, i, + LOG_TARGET_DEBUG(target, "i=%d; PTE @0x%" TARGET_PRIxADDR " = 0x%" PRIx64, i, pte_address, pte); - if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) + if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { + LOG_TARGET_ERROR(target, "invalid PTE @0x%" TARGET_PRIxADDR ": 0x%" PRIx64 + "; mode=%s; i=%d", pte_address, pte, info->name, i); return ERROR_FAIL; + } - if ((pte & PTE_R) || (pte & PTE_X)) /* Found leaf PTE. */ + if ((pte & PTE_R) || (pte & PTE_W) || (pte & PTE_X)) /* Found leaf PTE. */ break; i--; if (i < 0) break; - ppn_value = pte >> PTE_PPN_SHIFT; - table_address = ppn_value << RISCV_PGSHIFT; + ppn = pte >> PTE_PPN_SHIFT; + table_address = ppn << RISCV_PGSHIFT; } if (i < 0) { - LOG_ERROR("Couldn't find the PTE."); + LOG_TARGET_ERROR(target, "Couldn't find the PTE."); return ERROR_FAIL; } @@ -1659,15 +3187,125 @@ static int riscv_address_translate(struct target *target, *physical = virtual & (((target_addr_t)1 << info->va_bits) - 1); while (i < info->level) { - ppn_value = pte >> info->pte_ppn_shift[i]; - ppn_value &= info->pte_ppn_mask[i]; + ppn = pte >> info->pte_ppn_shift[i]; + ppn &= info->pte_ppn_mask[i]; *physical &= ~(((target_addr_t)info->pa_ppn_mask[i]) << info->pa_ppn_shift[i]); - *physical |= (ppn_value << info->pa_ppn_shift[i]); + *physical |= (ppn << info->pa_ppn_shift[i]); i++; } - LOG_DEBUG("0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, virtual, - *physical); + LOG_TARGET_DEBUG(target, "mode=%s; 0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, + info->name, virtual, *physical); + return ERROR_OK; +} + +/* Virtual to physical translation for hypervisor mode. */ +static int riscv_virt2phys_v(struct target *target, target_addr_t virtual, target_addr_t *physical) +{ + riscv_reg_t vsatp; + if (riscv_reg_get(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read vsatp register."); + return ERROR_FAIL; + } + /* vsatp is identical to satp, so we can use the satp macros. */ + unsigned int xlen = riscv_xlen(target); + int vsatp_mode = get_field(vsatp, RISCV_SATP_MODE(xlen)); + LOG_TARGET_DEBUG(target, "VS-stage translation mode: %d", vsatp_mode); + riscv_reg_t hgatp; + if (riscv_reg_get(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read hgatp register."); + return ERROR_FAIL; + } + int hgatp_mode = get_field(hgatp, RISCV_HGATP_MODE(xlen)); + LOG_TARGET_DEBUG(target, "G-stage translation mode: %d", hgatp_mode); + + const virt2phys_info_t *vsatp_info; + /* VS-stage address translation. */ + switch (vsatp_mode) { + case SATP_MODE_SV32: + vsatp_info = &sv32; + break; + case SATP_MODE_SV39: + vsatp_info = &sv39; + break; + case SATP_MODE_SV48: + vsatp_info = &sv48; + break; + case SATP_MODE_SV57: + vsatp_info = &sv57; + break; + case SATP_MODE_OFF: + vsatp_info = NULL; + LOG_TARGET_DEBUG(target, "vsatp mode is %d. No VS-stage translation. (vsatp: 0x%" PRIx64 ")", + vsatp_mode, vsatp); + break; + default: + LOG_TARGET_ERROR(target, + "vsatp mode %d is not supported. (vsatp: 0x%" PRIx64 ")", + vsatp_mode, vsatp); + return ERROR_FAIL; + } + + const virt2phys_info_t *hgatp_info; + /* G-stage address translation. */ + switch (hgatp_mode) { + case HGATP_MODE_SV32X4: + hgatp_info = &sv32x4; + break; + case HGATP_MODE_SV39X4: + hgatp_info = &sv39x4; + break; + case HGATP_MODE_SV48X4: + hgatp_info = &sv48x4; + break; + case HGATP_MODE_SV57X4: + hgatp_info = &sv57x4; + break; + case HGATP_MODE_OFF: + hgatp_info = NULL; + LOG_TARGET_DEBUG(target, "hgatp mode is %d. No G-stage translation. (hgatp: 0x%" PRIx64 ")", + hgatp_mode, hgatp); + break; + default: + LOG_TARGET_ERROR(target, + "hgatp mode %d is not supported. (hgatp: 0x%" PRIx64 ")", + hgatp_mode, hgatp); + return ERROR_FAIL; + } + + /* For any virtual memory access, the original virtual address is + * converted in the first stage by VS-level address translation, + * as controlled by the vsatp register, into a guest physical + * address. */ + target_addr_t guest_physical; + if (vsatp_info) { + /* When V=1, memory accesses that would normally bypass + * address translation are subject to G- stage address + * translation alone. This includes memory accesses made + * in support of VS-stage address translation, such as + * reads and writes of VS-level page tables. */ + + if (riscv_address_translate(target, + vsatp_info, get_field(vsatp, RISCV_SATP_PPN(xlen)), + hgatp_info, get_field(hgatp, RISCV_SATP_PPN(xlen)), + virtual, &guest_physical) != ERROR_OK) + return ERROR_FAIL; + } else { + guest_physical = virtual; + } + + /* The guest physical address is then converted in the second + * stage by guest physical address translation, as controlled by + * the hgatp register, into a supervisor physical address. */ + if (hgatp_info) { + if (riscv_address_translate(target, + hgatp_info, get_field(hgatp, RISCV_HGATP_PPN(xlen)), + NULL, 0, + guest_physical, physical) != ERROR_OK) + return ERROR_FAIL; + } else { + *physical = guest_physical; + } return ERROR_OK; } @@ -1675,71 +3313,191 @@ static int riscv_address_translate(struct target *target, static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_addr_t *physical) { bool enabled; - if (riscv_mmu(target, &enabled) == ERROR_OK) { - if (!enabled) - return ERROR_FAIL; - - if (riscv_address_translate(target, virtual, physical) == ERROR_OK) - return ERROR_OK; + if (riscv_mmu(target, &enabled) != ERROR_OK) + return ERROR_FAIL; + if (!enabled) { + *physical = virtual; + LOG_TARGET_DEBUG(target, "MMU is disabled. 0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, virtual, *physical); + return ERROR_OK; } - return ERROR_FAIL; + riscv_reg_t priv; + if (riscv_reg_get(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read priv register."); + return ERROR_FAIL; + } + + if (priv & VIRT_PRIV_V) + return riscv_virt2phys_v(target, virtual, physical); + + riscv_reg_t satp_value; + if (riscv_reg_get(target, &satp_value, GDB_REGNO_SATP) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read SATP register."); + return ERROR_FAIL; + } + + unsigned int xlen = riscv_xlen(target); + int satp_mode = get_field(satp_value, RISCV_SATP_MODE(xlen)); + const virt2phys_info_t *satp_info; + switch (satp_mode) { + case SATP_MODE_SV32: + satp_info = &sv32; + break; + case SATP_MODE_SV39: + satp_info = &sv39; + break; + case SATP_MODE_SV48: + satp_info = &sv48; + break; + case SATP_MODE_SV57: + satp_info = &sv57; + break; + case SATP_MODE_OFF: + LOG_TARGET_ERROR(target, "No translation or protection." + " (satp: 0x%" PRIx64 ")", satp_value); + return ERROR_FAIL; + default: + LOG_TARGET_ERROR(target, "The translation mode is not supported." + " (satp: 0x%" PRIx64 ")", satp_value); + return ERROR_FAIL; + } + + return riscv_address_translate(target, + satp_info, get_field(satp_value, RISCV_SATP_PPN(xlen)), + NULL, 0, + virtual, physical); +} + +static int check_virt_memory_access(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, bool is_write) +{ + const bool is_misaligned = address % size != 0; + // TODO: This assumes that size of each page is 4 KiB, which is not necessarily the case. + const bool crosses_page_boundary = RISCV_PGBASE(address + size * count - 1) != RISCV_PGBASE(address); + if (is_misaligned && crosses_page_boundary) { + LOG_TARGET_ERROR(target, "Mis-aligned memory %s (address=0x%" TARGET_PRIxADDR ", size=%d, count=%d)" + " would access an element across page boundary. This is not supported.", + is_write ? "write" : "read", address, size, count); + return ERROR_FAIL; + } + return ERROR_OK; } static int riscv_read_phys_memory(struct target *target, target_addr_t phys_address, uint32_t size, uint32_t count, uint8_t *buffer) { + const struct riscv_mem_access_args args = { + .address = phys_address, + .read_buffer = buffer, + .size = size, + .count = count, + .increment = size, + }; RISCV_INFO(r); - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - return r->read_memory(target, phys_address, size, count, buffer, size); -} - -static int riscv_read_memory(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer) -{ - if (count == 0) { - LOG_WARNING("0-length read from 0x%" TARGET_PRIxADDR, address); - return ERROR_OK; - } - - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - - target_addr_t physical_addr; - if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK) - address = physical_addr; - - RISCV_INFO(r); - return r->read_memory(target, address, size, count, buffer, size); + return r->access_memory(target, args); } static int riscv_write_phys_memory(struct target *target, target_addr_t phys_address, uint32_t size, uint32_t count, const uint8_t *buffer) { - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - struct target_type *tt = get_target_type(target); - return tt->write_memory(target, phys_address, size, count, buffer); + const struct riscv_mem_access_args args = { + .address = phys_address, + .write_buffer = buffer, + .size = size, + .count = count, + .increment = size, + }; + + RISCV_INFO(r); + return r->access_memory(target, args); +} + +static int riscv_rw_memory(struct target *target, const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_valid(args)); + + const bool is_write = riscv_mem_access_is_write(args); + if (args.count == 0) { + LOG_TARGET_WARNING(target, "0-length %s 0x%" TARGET_PRIxADDR, + is_write ? "write to" : "read from", args.address); + return ERROR_OK; + } + + bool mmu_enabled; + int result = riscv_mmu(target, &mmu_enabled); + if (result != ERROR_OK) + return result; + + RISCV_INFO(r); + if (!mmu_enabled) + return r->access_memory(target, args); + + result = check_virt_memory_access(target, args.address, + args.size, args.count, is_write); + if (result != ERROR_OK) + return result; + + uint32_t current_count = 0; + target_addr_t current_address = args.address; + while (current_count < args.count) { + target_addr_t physical_addr; + result = target->type->virt2phys(target, current_address, &physical_addr); + if (result != ERROR_OK) { + LOG_TARGET_ERROR(target, "Address translation failed."); + return result; + } + + /* TODO: For simplicity, this algorithm assumes the worst case - the smallest possible page size, + * which is 4 KiB. The algorithm can be improved to detect the real page size, and allow to use larger + * memory transfers and avoid extra unnecessary virt2phys address translations. */ + uint32_t chunk_count = MIN(args.count - current_count, + (RISCV_PGSIZE - RISCV_PGOFFSET(current_address)) + / args.size); + + struct riscv_mem_access_args current_access = args; + current_access.address = physical_addr; + current_access.count = chunk_count; + if (is_write) + current_access.write_buffer += current_count * args.size; + else + current_access.read_buffer += current_count * args.size; + + result = r->access_memory(target, current_access); + if (result != ERROR_OK) + return result; + + current_count += chunk_count; + current_address += chunk_count * args.size; + } + return ERROR_OK; +} + +static int riscv_read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + const struct riscv_mem_access_args args = { + .address = address, + .read_buffer = buffer, + .size = size, + .count = count, + .increment = size, + }; + + return riscv_rw_memory(target, args); } static int riscv_write_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { - if (count == 0) { - LOG_WARNING("0-length write to 0x%" TARGET_PRIxADDR, address); - return ERROR_OK; - } + const struct riscv_mem_access_args args = { + .address = address, + .write_buffer = buffer, + .size = size, + .count = count, + .increment = size, + }; - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - - target_addr_t physical_addr; - if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK) - address = physical_addr; - - struct target_type *tt = get_target_type(target); - return tt->write_memory(target, address, size, count, buffer); + return riscv_rw_memory(target, args); } static const char *riscv_get_gdb_arch(const struct target *target) @@ -1750,26 +3508,21 @@ static const char *riscv_get_gdb_arch(const struct target *target) case 64: return "riscv:rv64"; } - LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target)); + LOG_TARGET_ERROR(target, "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) + enum target_register_class reg_class, bool is_read) { - RISCV_INFO(r); - LOG_DEBUG("[%s] {%d} reg_class=%d, read=%d", - target_name(target), r->current_hartid, reg_class, read); + LOG_TARGET_DEBUG(target, "reg_class=%d, read=%d", reg_class, is_read); if (!target->reg_cache) { - LOG_ERROR("Target not initialized. Return ERROR_FAIL."); + LOG_TARGET_ERROR(target, "Target not initialized. Return ERROR_FAIL."); return ERROR_FAIL; } - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - switch (reg_class) { case REG_CLASS_GENERAL: *reg_list_size = 33; @@ -1778,7 +3531,7 @@ static int riscv_get_gdb_reg_list_internal(struct target *target, *reg_list_size = target->reg_cache->num_regs; break; default: - LOG_ERROR("Unsupported reg_class: %d", reg_class); + LOG_TARGET_ERROR(target, "Unsupported reg_class: %d", reg_class); return ERROR_FAIL; } @@ -1790,11 +3543,10 @@ static int riscv_get_gdb_reg_list_internal(struct target *target, assert(!target->reg_cache->reg_list[i].valid || target->reg_cache->reg_list[i].size > 0); (*reg_list)[i] = &target->reg_cache->reg_list[i]; - if (read && + if (is_read && target->reg_cache->reg_list[i].exist && !target->reg_cache->reg_list[i].valid) { - if (target->reg_cache->reg_list[i].type->get( - &target->reg_cache->reg_list[i]) != ERROR_OK) + if (target->reg_cache->reg_list[i].type->get(&target->reg_cache->reg_list[i]) != ERROR_OK) return ERROR_FAIL; } } @@ -1820,7 +3572,17 @@ static int riscv_get_gdb_reg_list(struct target *target, static int riscv_arch_state(struct target *target) { + assert(target->state == TARGET_HALTED); + const bool semihosting_active = target->semihosting && + target->semihosting->is_active; + LOG_USER("%s halted due to %s.%s", + target_name(target), + debug_reason_name(target), + semihosting_active ? " Semihosting is active." : ""); struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; + assert(tt->arch_state); return tt->arch_state(target); } @@ -1832,40 +3594,48 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, { RISCV_INFO(info); - if (num_mem_params > 0) { - LOG_ERROR("Memory parameters are not supported for RISC-V algorithms."); - return ERROR_FAIL; - } - if (target->state != TARGET_HALTED) { LOG_TARGET_ERROR(target, "not halted (run target algo)"); return ERROR_TARGET_NOT_HALTED; } + /* Write memory parameters to the target memory */ + for (int i = 0; i < num_mem_params; i++) { + if (mem_params[i].direction == PARAM_OUT || + mem_params[i].direction == PARAM_IN_OUT) { + int retval = target_write_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value); + if (retval != ERROR_OK) { + LOG_TARGET_ERROR(target, "Couldn't write input mem param into the memory, addr=0x%" TARGET_PRIxADDR + " size=0x%" PRIx32, mem_params[i].address, mem_params[i].size); + return retval; + } + } + } + /* Save registers */ struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", true); if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK) return ERROR_FAIL; uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size); - LOG_DEBUG("saved_pc=0x%" PRIx64, saved_pc); + LOG_TARGET_DEBUG(target, "saved_pc=0x%" PRIx64, saved_pc); uint64_t saved_regs[32]; for (int i = 0; i < num_reg_params; i++) { - LOG_DEBUG("save %s", reg_params[i].reg_name); + LOG_TARGET_DEBUG(target, "save %s", reg_params[i].reg_name); 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); + LOG_TARGET_ERROR(target, "Couldn't find register named '%s'", reg_params[i].reg_name); return ERROR_FAIL; } if (r->size != reg_params[i].size) { - LOG_ERROR("Register %s is %d bits instead of %d bits.", + LOG_TARGET_ERROR(target, "Register %s is %d bits instead of %d bits.", reg_params[i].reg_name, r->size, reg_params[i].size); return ERROR_FAIL; } if (r->number > GDB_REGNO_XPR31) { - LOG_ERROR("Only GPRs can be use as argument registers."); + LOG_TARGET_ERROR(target, "Only GPRs can be use as argument registers."); return ERROR_FAIL; } @@ -1879,38 +3649,22 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } } - /* Disable Interrupts before attempting to run the algorithm. */ - uint64_t current_mstatus; - uint8_t mstatus_bytes[8] = { 0 }; - - LOG_DEBUG("Disabling Interrupts"); - struct reg *reg_mstatus = register_get_by_name(target->reg_cache, - "mstatus", true); - if (!reg_mstatus) { - LOG_ERROR("Couldn't find mstatus!"); + riscv_reg_t current_mstatus; + if (riscv_interrupts_disable(target, ¤t_mstatus) != ERROR_OK) 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, set_field(current_mstatus, - ie_mask, 0)); - - reg_mstatus->type->set(reg_mstatus, mstatus_bytes); /* Run algorithm */ - LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point); - if (riscv_resume(target, false, entry_point, false, false, true) != ERROR_OK) + LOG_TARGET_DEBUG(target, "resume at 0x%" TARGET_PRIxADDR, entry_point); + if (riscv_resume(target, false, entry_point, false, true, true) != ERROR_OK) return ERROR_FAIL; int64_t start = timeval_ms(); while (target->state != TARGET_HALTED) { - LOG_DEBUG("poll()"); + LOG_TARGET_DEBUG(target, "poll()"); int64_t now = timeval_ms(); if (now - start > timeout_ms) { - LOG_ERROR("Algorithm timed out after %" PRId64 " ms.", now - start); + LOG_TARGET_ERROR(target, "Algorithm timed out after %" PRId64 " ms.", now - start); riscv_halt(target); old_or_new_riscv_poll(target); enum gdb_regno regnums[] = { @@ -1928,9 +3682,10 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, for (unsigned int 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) + if (riscv_reg_get(target, ®_value, regno) != ERROR_OK) break; - LOG_ERROR("%s = 0x%" PRIx64, gdb_regno_name(regno), reg_value); + + LOG_TARGET_ERROR(target, "%s = 0x%" PRIx64, riscv_reg_gdb_regno_name(target, regno), reg_value); } return ERROR_TARGET_TIMEOUT; } @@ -1940,23 +3695,22 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, return result; } - /* The current hart id might have been changed in poll(). */ - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; + /* TODO: The current hart id might have been changed in poll(). */ + /* if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; */ if (reg_pc->type->get(reg_pc) != ERROR_OK) return ERROR_FAIL; uint64_t final_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size); if (exit_point && final_pc != exit_point) { - LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%" + LOG_TARGET_ERROR(target, "PC ended up at 0x%" PRIx64 " instead of 0x%" TARGET_PRIxADDR, final_pc, exit_point); return ERROR_FAIL; } /* Restore Interrupts */ - LOG_DEBUG("Restoring Interrupts"); - buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus); - reg_mstatus->type->set(reg_mstatus, mstatus_bytes); + if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK) + return ERROR_FAIL; /* Restore registers */ uint8_t buf[8] = { 0 }; @@ -1969,20 +3723,35 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, reg_params[i].direction == PARAM_IN_OUT) { struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false); if (r->type->get(r) != ERROR_OK) { - LOG_ERROR("get(%s) failed", r->name); + LOG_TARGET_ERROR(target, "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); + LOG_TARGET_DEBUG(target, "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, saved_regs[r->number]); if (r->type->set(r, buf) != ERROR_OK) { - LOG_ERROR("set(%s) failed", r->name); + LOG_TARGET_ERROR(target, "set(%s) failed", r->name); return ERROR_FAIL; } } + /* Read memory parameters from the target memory */ + for (int i = 0; i < num_mem_params; i++) { + if (mem_params[i].direction == PARAM_IN || + mem_params[i].direction == PARAM_IN_OUT) { + int retval = target_read_buffer(target, mem_params[i].address, mem_params[i].size, + mem_params[i].value); + if (retval != ERROR_OK) { + LOG_TARGET_ERROR(target, "Couldn't read output mem param from the memory, " + "addr=0x%" TARGET_PRIxADDR " size=0x%" PRIx32, + mem_params[i].address, mem_params[i].size); + return retval; + } + } + } + return ERROR_OK; } @@ -1994,7 +3763,7 @@ static int riscv_checksum_memory(struct target *target, struct reg_param reg_params[2]; int retval; - LOG_DEBUG("address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count); + LOG_TARGET_DEBUG(target, "address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count); static const uint8_t riscv32_crc_code[] = { #include "../../../contrib/loaders/checksum/riscv32_crc.inc" @@ -2038,7 +3807,7 @@ static int riscv_checksum_memory(struct target *target, retval = target_write_buffer(target, crc_algorithm->address, crc_code_size, crc_code); if (retval != ERROR_OK) { - LOG_ERROR("Failed to write code to " TARGET_ADDR_FMT ": %d", + LOG_TARGET_ERROR(target, "Failed to write code to " TARGET_ADDR_FMT ": %d", crc_algorithm->address, retval); target_free_working_area(target, crc_algorithm); return retval; @@ -2060,74 +3829,153 @@ static int riscv_checksum_memory(struct target *target, if (retval == ERROR_OK) *checksum = buf_get_u32(reg_params[0].value, 0, 32); else - LOG_ERROR("error executing RISC-V CRC algorithm"); + LOG_TARGET_ERROR(target, "Error executing RISC-V CRC algorithm."); destroy_reg_param(®_params[0]); destroy_reg_param(®_params[1]); target_free_working_area(target, crc_algorithm); - LOG_DEBUG("checksum=0x%" PRIx32 ", result=%d", *checksum, retval); + LOG_TARGET_DEBUG(target, "checksum=0x%" PRIx32 ", result=%d", *checksum, retval); return retval; } /*** OpenOCD Helper Functions ***/ -enum riscv_poll_hart { - RPH_NO_CHANGE, - RPH_DISCOVERED_HALTED, - RPH_DISCOVERED_RUNNING, - RPH_ERROR +enum riscv_next_action { + RPH_NONE, + RPH_RESUME, + RPH_REMAIN_HALTED }; -static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid) +static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_action) { RISCV_INFO(r); - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) - return RPH_ERROR; - LOG_DEBUG("polling hart %d, target->state=%d", hartid, target->state); + LOG_TARGET_DEBUG(target, "polling, target->state=%d", target->state); + + *next_action = RPH_NONE; + + enum riscv_hart_state previous_riscv_state = 0; + enum target_state previous_target_state = target->state; + switch (target->state) { + case TARGET_UNKNOWN: + /* Special case, handled further down. */ + previous_riscv_state = RISCV_STATE_UNAVAILABLE; /* Need to assign something. */ + break; + case TARGET_RUNNING: + previous_riscv_state = RISCV_STATE_RUNNING; + break; + case TARGET_HALTED: + previous_riscv_state = RISCV_STATE_HALTED; + break; + case TARGET_RESET: + previous_riscv_state = RISCV_STATE_HALTED; + break; + case TARGET_DEBUG_RUNNING: + previous_riscv_state = RISCV_STATE_RUNNING; + break; + case TARGET_UNAVAILABLE: + previous_riscv_state = RISCV_STATE_UNAVAILABLE; + break; + } /* If OpenOCD thinks we're running but this hart is halted then it's time * to raise an event. */ - bool halted = riscv_is_halted(target); - if (target->state != TARGET_HALTED && halted) { - LOG_DEBUG(" triggered a halt"); - r->on_halt(target); - return RPH_DISCOVERED_HALTED; - } else if (target->state != TARGET_RUNNING && !halted) { - LOG_DEBUG(" triggered running"); - target->state = TARGET_RUNNING; - target->debug_reason = DBG_REASON_NOTHALTED; - return RPH_DISCOVERED_RUNNING; + enum riscv_hart_state state; + if (riscv_get_hart_state(target, &state) != ERROR_OK) + return ERROR_FAIL; + + if (state == RISCV_STATE_NON_EXISTENT) { + LOG_TARGET_ERROR(target, "Hart is non-existent!"); + return ERROR_FAIL; } - return RPH_NO_CHANGE; -} - -static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason) -{ - switch (halt_reason) { - case RISCV_HALT_BREAKPOINT: - target->debug_reason = DBG_REASON_BREAKPOINT; - break; - case RISCV_HALT_TRIGGER: - target->debug_reason = DBG_REASON_WATCHPOINT; - break; - case RISCV_HALT_INTERRUPT: - case RISCV_HALT_GROUP: - target->debug_reason = DBG_REASON_DBGRQ; - break; - case RISCV_HALT_SINGLESTEP: - target->debug_reason = DBG_REASON_SINGLESTEP; - break; - case RISCV_HALT_UNKNOWN: - target->debug_reason = DBG_REASON_UNDEFINED; - break; - case RISCV_HALT_ERROR: + if (state == RISCV_STATE_HALTED && timeval_ms() - r->last_activity > 100) { + /* If we've been idle for a while, flush the register cache. Just in case + * OpenOCD is going to be disconnected without shutting down cleanly. */ + if (riscv_reg_flush_all(target) != ERROR_OK) return ERROR_FAIL; } - LOG_DEBUG("[%s] debug_reason=%d", target_name(target), target->debug_reason); + + if (target->state == TARGET_UNKNOWN || state != previous_riscv_state) { + switch (state) { + case RISCV_STATE_HALTED: + if (previous_riscv_state == RISCV_STATE_UNAVAILABLE) + LOG_TARGET_INFO(target, "became available (halted)"); + + LOG_TARGET_DEBUG(target, " triggered a halt; previous_target_state=%d", + previous_target_state); + target->state = TARGET_HALTED; + enum riscv_halt_reason halt_reason = riscv_halt_reason(target); + if (set_debug_reason(target, halt_reason) != ERROR_OK) + return ERROR_FAIL; + + if (halt_reason == RISCV_HALT_EBREAK) { + int retval; + /* Detect if this EBREAK is a semihosting request. If so, handle it. */ + switch (riscv_semihosting(target, &retval)) { + case SEMIHOSTING_NONE: + break; + case SEMIHOSTING_WAITING: + /* This hart should remain halted. */ + *next_action = RPH_REMAIN_HALTED; + break; + case SEMIHOSTING_HANDLED: + /* This hart should be resumed, along with any other + * harts that halted due to haltgroups. */ + *next_action = RPH_RESUME; + return ERROR_OK; + case SEMIHOSTING_ERROR: + return retval; + } + } + + if (r->handle_became_halted && + r->handle_became_halted(target, previous_riscv_state) != ERROR_OK) + return ERROR_FAIL; + + /* We shouldn't do the callbacks yet. What if + * there are multiple harts that halted at the + * same time? We need to set debug reason on each + * of them before calling a callback, which is + * going to figure out the "current thread". */ + + r->halted_needs_event_callback = true; + if (previous_target_state == TARGET_DEBUG_RUNNING) + r->halted_callback_event = TARGET_EVENT_DEBUG_HALTED; + else + r->halted_callback_event = TARGET_EVENT_HALTED; + break; + + case RISCV_STATE_RUNNING: + if (previous_riscv_state == RISCV_STATE_UNAVAILABLE) + LOG_TARGET_INFO(target, "became available (running)"); + + LOG_TARGET_DEBUG(target, " triggered running"); + target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; + if (r->handle_became_running && + r->handle_became_running(target, previous_riscv_state) != ERROR_OK) + return ERROR_FAIL; + break; + + case RISCV_STATE_UNAVAILABLE: + LOG_TARGET_DEBUG(target, " became unavailable"); + LOG_TARGET_INFO(target, "became unavailable."); + target->state = TARGET_UNAVAILABLE; + if (r->handle_became_unavailable && + r->handle_became_unavailable(target, previous_riscv_state) != ERROR_OK) + return ERROR_FAIL; + break; + + case RISCV_STATE_NON_EXISTENT: + LOG_TARGET_ERROR(target, "Hart is non-existent!"); + target->state = TARGET_UNAVAILABLE; + break; + } + } + return ERROR_OK; } @@ -2138,7 +3986,7 @@ static int sample_memory(struct target *target) 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); + LOG_TARGET_DEBUG(target, "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); @@ -2157,8 +4005,8 @@ static int sample_memory(struct target *target) 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, + 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) @@ -2172,7 +4020,7 @@ static int sample_memory(struct target *target) exit: riscv_sample_buf_maybe_add_timestamp(target, false); if (result != ERROR_OK) { - LOG_INFO("Turning off memory sampling because it failed."); + LOG_TARGET_INFO(target, "Turning off memory sampling because it failed."); r->sample_config.enabled = false; } return result; @@ -2181,178 +4029,297 @@ exit: /*** OpenOCD Interface ***/ int riscv_openocd_poll(struct target *target) { - LOG_DEBUG("polling all harts"); - int halted_hart = -1; + LOG_TARGET_DEBUG(target, "Polling all harts."); + + struct riscv_info *i = riscv_info(target); + + struct list_head *targets; + + OOCD_LIST_HEAD(single_target_list); + struct target_list single_target_entry = { + .lh = {NULL, NULL}, + .target = target + }; if (target->smp) { - unsigned int should_remain_halted = 0; - unsigned int should_resume = 0; - struct target_list *list; - foreach_smp_target(list, target->smp_targets) { - struct target *t = list->target; - 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: - break; - case RPH_DISCOVERED_RUNNING: - t->state = TARGET_RUNNING; - t->debug_reason = DBG_REASON_NOTHALTED; - break; - case RPH_DISCOVERED_HALTED: - t->state = TARGET_HALTED; - enum riscv_halt_reason halt_reason = - riscv_halt_reason(t, r->current_hartid); - if (set_debug_reason(t, halt_reason) != ERROR_OK) - return ERROR_FAIL; - - if (halt_reason == RISCV_HALT_BREAKPOINT) { - int retval; - switch (riscv_semihosting(t, &retval)) { - case SEMIHOSTING_NONE: - case SEMIHOSTING_WAITING: - /* This hart should remain halted. */ - should_remain_halted++; - break; - case SEMIHOSTING_HANDLED: - /* This hart should be resumed, along with any other - * harts that halted due to haltgroups. */ - should_resume++; - break; - case SEMIHOSTING_ERROR: - return retval; - } - } else if (halt_reason != RISCV_HALT_GROUP) { - should_remain_halted++; - } - break; - - case RPH_ERROR: - return ERROR_FAIL; - } - } - - LOG_DEBUG("should_remain_halted=%d, should_resume=%d", - should_remain_halted, should_resume); - if (should_remain_halted && should_resume) { - LOG_WARNING("%d harts should remain halted, and %d should resume.", - should_remain_halted, should_resume); - } - if (should_remain_halted) { - LOG_DEBUG("halt all"); - riscv_halt(target); - } else if (should_resume) { - LOG_DEBUG("resume all"); - riscv_resume(target, true, 0, false, false, 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; - + targets = target->smp_targets; } else { - enum riscv_poll_hart out = riscv_poll_hart(target, - riscv_current_hartid(target)); - if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) { - if (target->state == TARGET_RUNNING) - sample_memory(target); - return ERROR_OK; - } else if (out == RPH_ERROR) { - return ERROR_FAIL; - } - - halted_hart = riscv_current_hartid(target); - LOG_DEBUG(" hart %d halted", halted_hart); - - enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart); - if (set_debug_reason(target, halt_reason) != ERROR_OK) - return ERROR_FAIL; - target->state = TARGET_HALTED; + /* Make a list that just contains a single target, so we can + * share code below. */ + list_add(&single_target_entry.lh, &single_target_list); + targets = &single_target_list; } - if (target->debug_reason == DBG_REASON_BREAKPOINT) { - int retval; - switch (riscv_semihosting(target, &retval)) { - case SEMIHOSTING_NONE: - case SEMIHOSTING_WAITING: - target_call_event_callbacks(target, TARGET_EVENT_HALTED); + unsigned int should_remain_halted = 0; + unsigned int should_resume = 0; + unsigned int halted = 0; + unsigned int running = 0; + unsigned int cause_groups = 0; + struct target_list *entry; + foreach_smp_target(entry, targets) { + struct target *t = entry->target; + struct riscv_info *info = riscv_info(t); + + /* Clear here just in case there were errors and we never got to + * check this flag further down. */ + info->halted_needs_event_callback = false; + + if (!target_was_examined(t)) + continue; + + enum riscv_next_action next_action; + if (riscv_poll_hart(t, &next_action) != ERROR_OK) + return ERROR_FAIL; + + switch (next_action) { + case RPH_NONE: + if (t->state == TARGET_HALTED) + halted++; + if (t->state == TARGET_RUNNING || + t->state == TARGET_DEBUG_RUNNING) + running++; break; - case SEMIHOSTING_HANDLED: - if (riscv_resume(target, true, 0, false, false, false) != ERROR_OK) - return ERROR_FAIL; + case RPH_REMAIN_HALTED: + should_remain_halted++; + break; + case RPH_RESUME: + should_resume++; break; - case SEMIHOSTING_ERROR: - return retval; } + } + + LOG_TARGET_DEBUG(target, "should_remain_halted=%d, should_resume=%d", + should_remain_halted, should_resume); + if (should_remain_halted && should_resume) { + LOG_TARGET_WARNING(target, "%d harts should remain halted, and %d should resume.", + should_remain_halted, should_resume); + } + if (should_remain_halted) { + LOG_TARGET_DEBUG(target, "halt all; should_remain_halted=%d", + should_remain_halted); + riscv_halt(target); + } else if (should_resume) { + LOG_TARGET_DEBUG(target, "resume all"); + riscv_resume(target, true, 0, 0, 0, false); + } else if (halted && running) { + LOG_TARGET_DEBUG(target, "SMP group is in inconsistent state: %u halted, %u running", + halted, running); + + /* The SMP group is in an inconsistent state - some harts in the group have halted + * whereas others are running. The reasons for that (and corresponding + * OpenOCD actions) could be: + * 1) The targets are in the process of halting due to halt groups + * but not all of them halted --> poll again so that the halt reason of every + * hart can be accurately determined (e.g. semihosting). + * 2) The targets do not support halt groups --> OpenOCD must halt + * the remaining harts by a standard halt request. + * 3) The hart states got out of sync for some other unknown reason (problem?). --> + * Same as previous - try to halt the harts by a standard halt request + * to get them back in sync. */ + + /* Detect if the harts are just in the process of halting due to a halt group */ + foreach_smp_target(entry, targets) + { + struct target *t = entry->target; + if (t->state == TARGET_HALTED) { + riscv_reg_t dcsr; + if (riscv_reg_get(t, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + if (get_field(dcsr, CSR_DCSR_CAUSE) == CSR_DCSR_CAUSE_GROUP) + cause_groups++; + else + /* This hart has halted due to something else than a halt group. + * Don't continue checking the rest - exit early. */ + break; + } + } + /* Condition: halted == cause_groups + * + * This condition indicates a paradox where: + * - All currently halted harts show CSR_DCSR_CAUSE_GROUP + * - However, no individual hart can be identified as the actual initiator of the halt condition + * + * Poll again so that the true halt reason can be discovered (e.g. CSR_DCSR_CAUSE_EBREAK) */ + if (halted == cause_groups) { + LOG_TARGET_DEBUG(target, "The harts appear to just be in the process of halting due to a halt group."); + if (i->halt_group_repoll_count < RISCV_HALT_GROUP_REPOLL_LIMIT) { + /* Wait a little, then re-poll. */ + i->halt_group_repoll_count++; + alive_sleep(10); + LOG_TARGET_DEBUG(target, "Re-polling the state of the SMP group."); + return riscv_openocd_poll(target); + } + /* We have already re-polled multiple times but the halt group is still inconsistent. */ + LOG_TARGET_DEBUG(target, "Re-polled the SMP group %d times it is still not in a consistent state.", + RISCV_HALT_GROUP_REPOLL_LIMIT); + } + + /* Halting the whole SMP group to bring it in sync. */ + LOG_TARGET_DEBUG(target, "halt all; halted=%d", + halted); + riscv_halt(target); } else { - target_call_event_callbacks(target, TARGET_EVENT_HALTED); + /* For targets that were discovered to be halted, call the + * appropriate callback. */ + foreach_smp_target(entry, targets) + { + struct target *t = entry->target; + struct riscv_info *info = riscv_info(t); + if (info->halted_needs_event_callback) { + target_call_event_callbacks(t, info->halted_callback_event); + info->halted_needs_event_callback = false; + } + } + } + + i->halt_group_repoll_count = 0; + + /* Call tick() for every hart. What happens in tick() is opaque to this + * layer. The reason it's outside the previous loop is that at this point + * the state of every hart has settled, so any side effects happening in + * tick() won't affect the delicate poll() code. */ + foreach_smp_target(entry, targets) { + struct target *t = entry->target; + struct riscv_info *info = riscv_info(t); + if (info->tick && info->tick(t) != ERROR_OK) + return ERROR_FAIL; + } + + /* Sample memory if any target is running. */ + foreach_smp_target(entry, targets) { + struct target *t = entry->target; + if (t->state == TARGET_RUNNING) { + sample_memory(target); + break; + } } return ERROR_OK; } -int riscv_openocd_step(struct target *target, bool current, - target_addr_t address, bool handle_breakpoints) +static int riscv_openocd_step_impl(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints, int handle_callbacks) { - LOG_DEBUG("stepping rtos hart"); + LOG_TARGET_DEBUG(target, "stepping hart"); - if (!current) - riscv_set_register(target, GDB_REGNO_PC, address); - - riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0}; - if (disable_triggers(target, trigger_state) != ERROR_OK) - return ERROR_FAIL; - - int out = riscv_step_rtos_hart(target); - if (out != ERROR_OK) { - LOG_ERROR("unable to step rtos hart"); - return out; + if (!current) { + if (riscv_reg_set(target, GDB_REGNO_PC, address) != ERROR_OK) + return ERROR_FAIL; } - register_cache_invalidate(target->reg_cache); + struct breakpoint *breakpoint = NULL; + /* the front-end may request us not to handle breakpoints */ + if (handle_breakpoints) { + if (current) { + if (riscv_reg_get(target, &address, GDB_REGNO_PC) != ERROR_OK) + return ERROR_FAIL; + } + breakpoint = breakpoint_find(target, address); + if (breakpoint && (riscv_remove_breakpoint(target, breakpoint) != ERROR_OK)) + return ERROR_FAIL; + } - if (enable_triggers(target, trigger_state) != ERROR_OK) + if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; - target->state = TARGET_RUNNING; - target_call_event_callbacks(target, TARGET_EVENT_RESUMED); - target->state = TARGET_HALTED; - target->debug_reason = DBG_REASON_SINGLESTEP; - target_call_event_callbacks(target, TARGET_EVENT_HALTED); - return out; + RISCV_INFO(r); + bool *wps_to_enable = calloc(r->trigger_count, sizeof(*wps_to_enable)); + if (disable_watchpoints(target, wps_to_enable) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to temporarily disable " + "watchpoints before single-step."); + return ERROR_FAIL; + } + + bool success = true; + riscv_reg_t current_mstatus; + RISCV_INFO(info); + + if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY) { + /* Disable Interrupts before stepping. */ + if (riscv_interrupts_disable(target, ¤t_mstatus) != ERROR_OK) { + success = false; + LOG_TARGET_ERROR(target, "Unable to disable interrupts."); + goto _exit; + } + } + + if (riscv_step_rtos_hart(target) != ERROR_OK) { + success = false; + LOG_TARGET_ERROR(target, "Unable to step rtos hart."); + } + + if (riscv_reg_cache_any_dirty(target, LOG_LVL_ERROR)) { + /* If this happens, it means there is a bug in the previous + * register-flushing algorithm: not all registers were flushed + * back to the target prior to single-step. */ + LOG_TARGET_ERROR(target, + "BUG: registers should have been flushed by this point."); + } + + riscv_reg_cache_invalidate_all(target); + + if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY) + if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK) { + success = false; + LOG_TARGET_ERROR(target, "Unable to restore interrupts."); + } + +_exit: + if (enable_watchpoints(target, wps_to_enable) != ERROR_OK) { + success = false; + LOG_TARGET_ERROR(target, "Failed to re-enable watchpoints " + "after single-step."); + } + + if (breakpoint && (riscv_add_breakpoint(target, breakpoint) != ERROR_OK)) { + success = false; + LOG_TARGET_ERROR(target, "Unable to restore the disabled breakpoint."); + } + + if (success) { + target->state = TARGET_RUNNING; + if (handle_callbacks) + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + + target->state = TARGET_HALTED; + target->debug_reason = DBG_REASON_SINGLESTEP; + if (handle_callbacks) + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + + return success ? ERROR_OK : ERROR_FAIL; +} + +int riscv_openocd_step(struct target *target, bool current, + target_addr_t address, bool handle_breakpoints) +{ + return riscv_openocd_step_impl(target, current, address, handle_breakpoints, + true /* handle_callbacks */); } /* Command Handlers */ COMMAND_HANDLER(riscv_set_command_timeout_sec) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } + int timeout = atoi(CMD_ARGV[0]); if (timeout <= 0) { LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]); return ERROR_FAIL; } - riscv_command_timeout_sec = timeout; + riscv_command_timeout_sec_value = timeout; return ERROR_OK; } COMMAND_HANDLER(riscv_set_reset_timeout_sec) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + LOG_WARNING("The command 'riscv set_reset_timeout_sec' is deprecated! Please, use 'riscv set_command_timeout_sec'."); + if (CMD_ARGC != 1) return ERROR_COMMAND_SYNTAX_ERROR; - } + int timeout = atoi(CMD_ARGV[0]); if (timeout <= 0) { LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]); @@ -2371,9 +4338,10 @@ COMMAND_HANDLER(riscv_set_mem_access) 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; + if (CMD_ARGC < 1 || CMD_ARGC > RISCV_MEM_ACCESS_MAX_METHODS_NUM) { + command_print(CMD, "Command takes 1 to %d parameters", + RISCV_MEM_ACCESS_MAX_METHODS_NUM); + return ERROR_COMMAND_ARGUMENT_INVALID; } /* Check argument validity */ @@ -2396,8 +4364,7 @@ COMMAND_HANDLER(riscv_set_mem_access) } /* 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; + r->num_enabled_mem_access_methods = CMD_ARGC; 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; @@ -2408,91 +4375,106 @@ COMMAND_HANDLER(riscv_set_mem_access) } /* Reset warning flags */ - r->mem_access_progbuf_warn = true; - r->mem_access_sysbus_warn = true; - r->mem_access_abstract_warn = true; + for (size_t i = 0; i < RISCV_MEM_ACCESS_MAX_METHODS_NUM; ++i) + r->mem_access_warn[i] = true; return ERROR_OK; } -COMMAND_HANDLER(riscv_set_enable_virtual) + +static bool parse_csr_address(const char *reg_address_str, unsigned int *reg_addr) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); - return ERROR_COMMAND_SYNTAX_ERROR; + *reg_addr = -1; + /* skip initial spaces */ + while (isspace(reg_address_str[0])) + ++reg_address_str; + /* try to detect if string starts with 0x or 0X */ + bool is_hex_address = strncmp(reg_address_str, "0x", 2) == 0 || + strncmp(reg_address_str, "0X", 2) == 0; + + unsigned int scanned_chars; + if (is_hex_address) { + reg_address_str += 2; + if (sscanf(reg_address_str, "%x%n", reg_addr, &scanned_chars) != 1) + return false; + } else { + /* If we are here and register address string starts with zero, this is + * an indication that most likely user has an incorrect input because: + * - decimal numbers typically do not start with "0" + * - octals are not supported by our interface + * - hexadecimal numbers should have "0x" prefix + * Thus such input is rejected. */ + if (reg_address_str[0] == '0' && strlen(reg_address_str) > 1) + return false; + if (sscanf(reg_address_str, "%u%n", reg_addr, &scanned_chars) != 1) + return false; } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virtual); - return ERROR_OK; + return scanned_chars == strlen(reg_address_str); } -static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val) +static int parse_reg_ranges_impl(struct list_head *ranges, char *args, + const char *reg_type, unsigned int max_val, char ** const name_buffer) { - char *args = strdup(tcl_arg); - if (!args) - return ERROR_FAIL; - - /* For backward compatibility, allow multiple parameters within one TCL argument, separated by ',' */ - char *arg = strtok(args, ","); - while (arg) { + /* For backward compatibility, allow multiple parameters within one TCL + * argument, separated by ',' */ + for (char *arg = strtok(args, ","); arg; arg = strtok(NULL, ",")) { unsigned int low = 0; unsigned int high = 0; 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)) { + if (!parse_csr_address(arg, &low)) { 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); + *dash = '\0'; + if (!parse_csr_address(arg, &low)) { + LOG_ERROR("Failed to parse '%s' - not a valid decimal or hexadecimal number.", + arg); 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); + const char *high_num_in = dash + 1; + if (!parse_csr_address(high_num_in, &high)) { + LOG_ERROR("Failed to parse '%s' - not a valid decimal or hexadecimal number.", + high_num_in); 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); + *equals = '\0'; + if (!parse_csr_address(arg, &low)) { + LOG_ERROR("Failed to parse '%s' - not a valid decimal or hexadecimal number.", + arg); return ERROR_COMMAND_SYNTAX_ERROR; } - name = calloc(1, strlen(equals) + strlen(reg_type) + 2); + const char * const reg_name_in = equals + 1; + const size_t reg_type_len = strlen(reg_type); + /* format is: _\0 */ + *name_buffer = calloc(1, strlen(reg_name_in) + reg_type_len + 2); + name = *name_buffer; if (!name) { - LOG_ERROR("Failed to allocate register name."); - free(args); + LOG_ERROR("Out of memory"); return ERROR_FAIL; } - - /* Register prefix: "csr_" or "custom_" */ strcpy(name, reg_type); - name[strlen(reg_type)] = '_'; + name[reg_type_len] = '_'; - 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); + unsigned int scanned_chars; + char *scan_dst = name + strlen(reg_type) + 1; + if (sscanf(reg_name_in, "%[_a-zA-Z0-9]%n", scan_dst, &scanned_chars) != 1 || + scanned_chars != strlen(reg_name_in)) { + LOG_ERROR("Invalid characters in register name '%s'.", reg_name_in); return ERROR_COMMAND_SYNTAX_ERROR; } } else { @@ -2501,19 +4483,18 @@ static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const cha return ERROR_COMMAND_SYNTAX_ERROR; } - high = high > low ? high : low; + high = MAX(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); + LOG_ERROR("Cannot expose %s register number 0x%x, maximum allowed value is 0x%x.", + reg_type, high, max_val); 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 (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); @@ -2524,45 +4505,53 @@ static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const cha if (entry->name && name && (strcasecmp(entry->name, name) == 0)) { LOG_ERROR("Duplicate register name \"%s\" found.", name); - free(name); - free(args); return ERROR_FAIL; } } range_list_t *range = calloc(1, sizeof(range_list_t)); if (!range) { - LOG_ERROR("Failed to allocate range list."); - free(name); - free(args); + LOG_ERROR("Out of memory"); return ERROR_FAIL; } range->low = low; range->high = high; range->name = name; + /* ownership over name_buffer contents is transferred to list item here */ + *name_buffer = NULL; list_add(&range->list, ranges); - - arg = strtok(NULL, ","); } - free(args); return ERROR_OK; } +static int parse_reg_ranges(struct list_head *ranges, const char *tcl_arg, + const char *reg_type, unsigned int max_val) +{ + char *args = strdup(tcl_arg); + if (!args) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + char *name_buffer = NULL; + int result = parse_reg_ranges_impl(ranges, args, reg_type, max_val, &name_buffer); + free(name_buffer); + free(args); + return result; +} + COMMAND_HANDLER(riscv_set_expose_csrs) { - if (CMD_ARGC == 0) { - LOG_ERROR("Command expects parameters"); + if (CMD_ARGC == 0) return ERROR_COMMAND_SYNTAX_ERROR; - } struct target *target = get_current_target(CMD_CTX); RISCV_INFO(info); int ret = ERROR_OK; for (unsigned int i = 0; i < CMD_ARGC; i++) { - ret = parse_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff); + ret = parse_reg_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff); if (ret != ERROR_OK) break; } @@ -2572,17 +4561,33 @@ COMMAND_HANDLER(riscv_set_expose_csrs) COMMAND_HANDLER(riscv_set_expose_custom) { - if (CMD_ARGC == 0) { - LOG_ERROR("Command expects parameters"); + if (CMD_ARGC == 0) return ERROR_COMMAND_SYNTAX_ERROR; - } struct target *target = get_current_target(CMD_CTX); RISCV_INFO(info); int ret = ERROR_OK; for (unsigned int i = 0; i < CMD_ARGC; i++) { - ret = parse_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff); + ret = parse_reg_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff); + if (ret != ERROR_OK) + break; + } + + return ret; +} + +COMMAND_HANDLER(riscv_hide_csrs) +{ + if (CMD_ARGC == 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(info); + int ret = ERROR_OK; + + for (unsigned int i = 0; i < CMD_ARGC; i++) { + ret = parse_reg_ranges(&info->hide_csr, CMD_ARGV[i], "csr", 0xfff); if (ret != ERROR_OK) break; } @@ -2593,14 +4598,10 @@ COMMAND_HANDLER(riscv_set_expose_custom) COMMAND_HANDLER(riscv_authdata_read) { unsigned int index = 0; - if (CMD_ARGC == 0) { - /* nop */ - } else if (CMD_ARGC == 1) { + if (CMD_ARGC == 1) COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index); - } else { - LOG_ERROR("Command takes at most one parameter"); + else if (CMD_ARGC != 0) return ERROR_COMMAND_SYNTAX_ERROR; - } struct target *target = get_current_target(CMD_CTX); if (!target) { @@ -2610,7 +4611,7 @@ COMMAND_HANDLER(riscv_authdata_read) RISCV_INFO(r); if (!r) { - LOG_ERROR("riscv_info is NULL!"); + LOG_TARGET_ERROR(target, "riscv_info is NULL!"); return ERROR_FAIL; } @@ -2620,10 +4621,10 @@ COMMAND_HANDLER(riscv_authdata_read) return ERROR_FAIL; command_print_sameline(CMD, "0x%08" PRIx32, value); return ERROR_OK; - } else { - LOG_ERROR("authdata_read is not implemented for this target."); - return ERROR_FAIL; } + + LOG_TARGET_ERROR(target, "authdata_read is not implemented for this target."); + return ERROR_FAIL; } COMMAND_HANDLER(riscv_authdata_write) @@ -2645,76 +4646,153 @@ COMMAND_HANDLER(riscv_authdata_write) RISCV_INFO(r); if (!r->authdata_write) { - LOG_ERROR("authdata_write is not implemented for this target."); + LOG_TARGET_ERROR(target, "authdata_write is not implemented for this target."); return ERROR_FAIL; } return r->authdata_write(target, value, index); } -COMMAND_HANDLER(riscv_dmi_read) +uint32_t riscv_get_dmi_address(const struct target *target, uint32_t dm_address) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes 1 parameter"); - return ERROR_COMMAND_SYNTAX_ERROR; - } + assert(target); + RISCV_INFO(r); + if (!r || !r->get_dmi_address) + return dm_address; + return r->get_dmi_address(target, dm_address); +} - struct target *target = get_current_target(CMD_CTX); +static int riscv_dmi_read(struct target *target, uint32_t *value, uint32_t address) +{ if (!target) { LOG_ERROR("target is NULL!"); return ERROR_FAIL; } - RISCV_INFO(r); if (!r) { - LOG_ERROR("riscv_info is NULL!"); + LOG_TARGET_ERROR(target, "riscv_info is NULL!"); return ERROR_FAIL; } - - if (r->dmi_read) { - uint32_t address, value; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address); - if (r->dmi_read(target, &value, address) != ERROR_OK) - return ERROR_FAIL; - command_print(CMD, "0x%" PRIx32, value); - return ERROR_OK; - } else { - LOG_ERROR("dmi_read is not implemented for this target."); + if (!r->dmi_read) { + LOG_TARGET_ERROR(target, "dmi_read is not implemented."); return ERROR_FAIL; } + return r->dmi_read(target, value, address); } - -COMMAND_HANDLER(riscv_dmi_write) +static int riscv_dmi_write(struct target *target, uint32_t dmi_address, uint32_t value) { - if (CMD_ARGC != 2) { - LOG_ERROR("Command takes exactly 2 arguments"); - return ERROR_COMMAND_SYNTAX_ERROR; - } - - struct target *target = get_current_target(CMD_CTX); - RISCV_INFO(r); - - uint32_t address, value; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address); - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); - - if (r->dmi_write) { - return r->dmi_write(target, address, value); - } else { - LOG_ERROR("dmi_write is not implemented for this target."); + if (!target) { + LOG_ERROR("target is NULL!"); return ERROR_FAIL; } + RISCV_INFO(r); + if (!r) { + LOG_TARGET_ERROR(target, "riscv_info is NULL!"); + return ERROR_FAIL; + } + if (!r->dmi_write) { + LOG_TARGET_ERROR(target, "dmi_write is not implemented."); + return ERROR_FAIL; + } + const int result = r->dmi_write(target, dmi_address, value); + /* Invalidate our cached progbuf copy: + * - if the user tinkered directly with a progbuf register + * - if debug module was reset, in which case progbuf registers + * may not retain their value. + * FIXME: If there are multiple DMs on a single TAP, it is possible to + * clobber progbuf or reset the DM of another target. + */ + const bool progbuf_touched = + (dmi_address >= riscv_get_dmi_address(target, DM_PROGBUF0) && + dmi_address <= riscv_get_dmi_address(target, DM_PROGBUF15)); + const bool dm_deactivated = + (dmi_address == riscv_get_dmi_address(target, DM_DMCONTROL) && + (value & DM_DMCONTROL_DMACTIVE) == 0); + if (progbuf_touched || dm_deactivated) { + if (r->invalidate_cached_progbuf) { + /* Here the return value of invalidate_cached_progbuf() + * is ignored. It is okay to do so for now, since the + * only case an error is returned is a failure to + * assign a DM to the target, which would have already + * caused an error during dmi_write(). + * FIXME: invalidate_cached_progbuf() should be void. + */ + r->invalidate_cached_progbuf(target); + } else { + LOG_TARGET_DEBUG(target, + "invalidate_cached_progbuf() is not implemented."); + } + } + return result; +} + +COMMAND_HANDLER(handle_riscv_dmi_read) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t dmi_address; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dmi_address); + + struct target * const target = get_current_target(CMD_CTX); + uint32_t value; + const int result = riscv_dmi_read(target, &value, dmi_address); + if (result == ERROR_OK) + command_print(CMD, "0x%" PRIx32, value); + return result; +} + +COMMAND_HANDLER(handle_riscv_dmi_write) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t dmi_address, value; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dmi_address); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + struct target * const target = get_current_target(CMD_CTX); + return riscv_dmi_write(target, dmi_address, value); +} + +COMMAND_HANDLER(handle_riscv_dm_read) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t dm_address; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dm_address); + + struct target * const target = get_current_target(CMD_CTX); + uint32_t value; + const int result = riscv_dmi_read(target, &value, + riscv_get_dmi_address(target, dm_address)); + if (result == ERROR_OK) + command_print(CMD, "0x%" PRIx32, value); + return result; +} + +COMMAND_HANDLER(handle_riscv_dm_write) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t dm_address, value; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dm_address); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + struct target * const target = get_current_target(CMD_CTX); + return riscv_dmi_write(target, riscv_get_dmi_address(target, dm_address), + value); } COMMAND_HANDLER(riscv_reset_delays) { int wait = 0; - if (CMD_ARGC > 1) { - LOG_ERROR("Command takes at most one argument"); + if (CMD_ARGC > 1) return ERROR_COMMAND_SYNTAX_ERROR; - } if (CMD_ARGC == 1) COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], wait); @@ -2727,10 +4805,8 @@ COMMAND_HANDLER(riscv_reset_delays) COMMAND_HANDLER(riscv_set_ir) { - if (CMD_ARGC != 2) { - LOG_ERROR("Command takes exactly 2 arguments"); + if (CMD_ARGC != 2) return ERROR_COMMAND_SYNTAX_ERROR; - } uint32_t value; COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); @@ -2749,10 +4825,8 @@ COMMAND_HANDLER(riscv_set_ir) COMMAND_HANDLER(riscv_resume_order) { - if (CMD_ARGC > 1) { - LOG_ERROR("Command takes at most one argument"); + if (CMD_ARGC > 1) return ERROR_COMMAND_SYNTAX_ERROR; - } if (!strcmp(CMD_ARGV[0], "normal")) { resume_order = RO_NORMAL; @@ -2768,18 +4842,23 @@ COMMAND_HANDLER(riscv_resume_order) COMMAND_HANDLER(riscv_use_bscan_tunnel) { - int irwidth = 0; + uint8_t irwidth = 0; int tunnel_type = BSCAN_TUNNEL_NESTED_TAP; - if (CMD_ARGC > 2) { - LOG_ERROR("Command takes at most two arguments"); + if (CMD_ARGC < 1 || CMD_ARGC > 2) return ERROR_COMMAND_SYNTAX_ERROR; - } else if (CMD_ARGC == 1) { - COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], irwidth); - } else if (CMD_ARGC == 2) { - COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], irwidth); - COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tunnel_type); + + if (CMD_ARGC >= 1) { + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], irwidth); + assert(BSCAN_TUNNEL_IR_WIDTH_NBITS < 8); + if (irwidth >= (uint8_t)1 << BSCAN_TUNNEL_IR_WIDTH_NBITS) { + command_print(CMD, "'value' does not fit into %d bits.", + BSCAN_TUNNEL_IR_WIDTH_NBITS); + return ERROR_COMMAND_ARGUMENT_OVERFLOW; + } } + if (CMD_ARGC == 2) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tunnel_type); if (tunnel_type == BSCAN_TUNNEL_NESTED_TAP) LOG_INFO("Nested Tap based Bscan Tunnel Selected"); else if (tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) @@ -2792,53 +4871,522 @@ COMMAND_HANDLER(riscv_use_bscan_tunnel) return ERROR_OK; } -COMMAND_HANDLER(riscv_set_enable_virt2phys) +COMMAND_HANDLER(riscv_set_bscan_tunnel_ir) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + int ir_id = 0; + + if (CMD_ARGC > 1) return ERROR_COMMAND_SYNTAX_ERROR; + + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ir_id); + + LOG_INFO("Bscan tunnel IR 0x%x selected", ir_id); + + bscan_tunnel_ir_id = ir_id; + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_set_maskisr) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(info); + + static const struct jim_nvp nvp_maskisr_modes[] = { + { .name = "off", .value = RISCV_ISRMASK_OFF }, + { .name = "steponly", .value = RISCV_ISRMASK_STEPONLY }, + { .name = NULL, .value = -1 }, + }; + const struct jim_nvp *n; + + if (CMD_ARGC > 0) { + n = jim_nvp_name2value_simple(nvp_maskisr_modes, CMD_ARGV[0]); + if (!n->name) + return ERROR_COMMAND_SYNTAX_ERROR; + info->isrmask_mode = n->value; + } else { + n = jim_nvp_value2name_simple(nvp_maskisr_modes, info->isrmask_mode); + command_print(CMD, "riscv interrupt mask %s", n->name); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_set_autofence) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "autofence: %s", r->autofence ? "on" : "off"); + return ERROR_OK; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->autofence); + return ERROR_OK; + } + + return ERROR_COMMAND_SYNTAX_ERROR; +} + +COMMAND_HELPER(ebreakx_deprecation_helper, enum riscv_priv_mode mode) +{ + struct target * const target = get_current_target(CMD_CTX); + struct riscv_private_config * const config = riscv_private_config(target); + const char *mode_str; + switch (mode) { + case RISCV_MODE_M: + mode_str = "m"; + break; + case RISCV_MODE_S: + mode_str = "s"; + break; + case RISCV_MODE_U: + mode_str = "u"; + break; + default: + assert(0 && "Unexpected execution mode"); + mode_str = "unexpected"; + } + if (CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + if (CMD_ARGC == 0) { + LOG_WARNING("DEPRECATED! use '%s cget -ebreak' not '%s'", + target_name(target), CMD_NAME); + command_print(CMD, "riscv_ebreak%s enabled: %s", mode_str, + config->dcsr_ebreak_fields[mode] ? "on" : "off"); + return ERROR_OK; + } + assert(CMD_ARGC == 1); + command_print(CMD, "DEPRECATED! use '%s configure -ebreak %s' not '%s'", + target_name(target), mode_str, CMD_NAME); + bool ebreak_ctl; + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], ebreak_ctl); + config->dcsr_ebreak_fields[mode] = ebreak_ctl; + switch (mode) { + case RISCV_MODE_S: + config->dcsr_ebreak_fields[RISCV_MODE_VS] = ebreak_ctl; + break; + case RISCV_MODE_U: + config->dcsr_ebreak_fields[RISCV_MODE_VU] = ebreak_ctl; + break; + default: + break; } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virt2phys); return ERROR_OK; } COMMAND_HANDLER(riscv_set_ebreakm) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); - return ERROR_COMMAND_SYNTAX_ERROR; - } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreakm); - return ERROR_OK; + return CALL_COMMAND_HANDLER(ebreakx_deprecation_helper, + RISCV_MODE_M); } COMMAND_HANDLER(riscv_set_ebreaks) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); - return ERROR_COMMAND_SYNTAX_ERROR; - } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaks); - return ERROR_OK; + return CALL_COMMAND_HANDLER(ebreakx_deprecation_helper, + RISCV_MODE_S); } COMMAND_HANDLER(riscv_set_ebreaku) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + return CALL_COMMAND_HANDLER(ebreakx_deprecation_helper, + RISCV_MODE_U); +} + +COMMAND_HELPER(riscv_clear_trigger, int trigger_id, const char *name) +{ + struct target *target = get_current_target(CMD_CTX); + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (find_first_trigger_by_id(target, trigger_id) < 0) { + LOG_TARGET_ERROR(target, "No %s is set. Nothing to clear.", name); + return ERROR_FAIL; + } + return remove_trigger(target, trigger_id); +} + +COMMAND_HANDLER(riscv_itrigger) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct target *target = get_current_target(CMD_CTX); + const int ITRIGGER_UNIQUE_ID = -CSR_TDATA1_TYPE_ITRIGGER; + + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + + if (!strcmp(CMD_ARGV[0], "set")) { + if (find_first_trigger_by_id(target, ITRIGGER_UNIQUE_ID) >= 0) { + LOG_TARGET_ERROR(target, "An itrigger is already set, and OpenOCD " + "doesn't support setting more than one at a time."); + return ERROR_FAIL; + } + bool vs = false; + bool vu = false; + bool nmi = false; + bool m = false; + bool s = false; + bool u = false; + riscv_reg_t interrupts = 0; + + for (unsigned int i = 1; i < CMD_ARGC; i++) { + if (!strcmp(CMD_ARGV[i], "vs")) + vs = true; + else if (!strcmp(CMD_ARGV[i], "vu")) + vu = true; + else if (!strcmp(CMD_ARGV[i], "nmi")) + nmi = true; + else if (!strcmp(CMD_ARGV[i], "m")) + m = true; + else if (!strcmp(CMD_ARGV[i], "s")) + s = true; + else if (!strcmp(CMD_ARGV[i], "u")) + u = true; + else + COMMAND_PARSE_NUMBER(u64, CMD_ARGV[i], interrupts); + } + if (!nmi && interrupts == 0) { + LOG_ERROR("Doesn't make sense to set itrigger with " + "mie_bits=0 and without nmi."); + return ERROR_FAIL; + } else if (!vs && !vu && !m && !s && !u) { + LOG_ERROR("Doesn't make sense to set itrigger without at " + "least one of vs, vu, m, s, or u."); + return ERROR_FAIL; + } + int result = maybe_add_trigger_t4(target, vs, vu, nmi, m, s, u, interrupts, ITRIGGER_UNIQUE_ID); + if (result != ERROR_OK) + LOG_TARGET_ERROR(target, "Failed to set requested itrigger."); + return result; + + } else if (!strcmp(CMD_ARGV[0], "clear")) { + return riscv_clear_trigger(CMD, ITRIGGER_UNIQUE_ID, "itrigger"); + + } else { + LOG_ERROR("First argument must be either 'set' or 'clear'."); return ERROR_COMMAND_SYNTAX_ERROR; } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaku); return ERROR_OK; } +COMMAND_HANDLER(riscv_icount) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct target *target = get_current_target(CMD_CTX); + const int ICOUNT_UNIQUE_ID = -CSR_TDATA1_TYPE_ICOUNT; + + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + + if (!strcmp(CMD_ARGV[0], "set")) { + if (find_first_trigger_by_id(target, ICOUNT_UNIQUE_ID) >= 0) { + LOG_TARGET_ERROR(target, "An icount trigger is already set, and OpenOCD " + "doesn't support setting more than one at a time."); + return ERROR_FAIL; + } + bool vs = false; + bool vu = false; + bool m = false; + bool s = false; + bool u = false; + bool pending = false; + unsigned int count = 0; + + for (unsigned int i = 1; i < CMD_ARGC; i++) { + if (!strcmp(CMD_ARGV[i], "vs")) + vs = true; + else if (!strcmp(CMD_ARGV[i], "vu")) + vu = true; + else if (!strcmp(CMD_ARGV[i], "pending")) + pending = true; + else if (!strcmp(CMD_ARGV[i], "m")) + m = true; + else if (!strcmp(CMD_ARGV[i], "s")) + s = true; + else if (!strcmp(CMD_ARGV[i], "u")) + u = true; + else + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[i], count); + } + if (count == 0) { + LOG_ERROR("Doesn't make sense to set icount trigger with " + "count=0."); + return ERROR_FAIL; + } else if (!vs && !vu && !m && !s && !u) { + LOG_ERROR("Doesn't make sense to set itrigger without at " + "least one of vs, vu, m, s, or u."); + return ERROR_FAIL; + } + int result = maybe_add_trigger_t3(target, vs, vu, m, s, u, pending, count, ICOUNT_UNIQUE_ID); + if (result != ERROR_OK) + LOG_TARGET_ERROR(target, "Failed to set requested icount trigger."); + return result; + + } else if (!strcmp(CMD_ARGV[0], "clear")) { + return riscv_clear_trigger(CMD, ICOUNT_UNIQUE_ID, "icount trigger"); + + } else { + LOG_ERROR("First argument must be either 'set' or 'clear'."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_etrigger) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct target *target = get_current_target(CMD_CTX); + const int ETRIGGER_UNIQUE_ID = -CSR_TDATA1_TYPE_ETRIGGER; + + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + + if (!strcmp(CMD_ARGV[0], "set")) { + if (find_first_trigger_by_id(target, ETRIGGER_UNIQUE_ID) >= 0) { + LOG_TARGET_ERROR(target, "An etrigger is already set, and OpenOCD " + "doesn't support setting more than one at a time."); + return ERROR_FAIL; + } + bool vs = false; + bool vu = false; + bool m = false; + bool s = false; + bool u = false; + riscv_reg_t exception_codes = 0; + + for (unsigned int i = 1; i < CMD_ARGC; i++) { + if (!strcmp(CMD_ARGV[i], "vs")) + vs = true; + else if (!strcmp(CMD_ARGV[i], "vu")) + vu = true; + else if (!strcmp(CMD_ARGV[i], "m")) + m = true; + else if (!strcmp(CMD_ARGV[i], "s")) + s = true; + else if (!strcmp(CMD_ARGV[i], "u")) + u = true; + else + COMMAND_PARSE_NUMBER(u64, CMD_ARGV[i], exception_codes); + } + if (exception_codes == 0) { + LOG_ERROR("Doesn't make sense to set etrigger with " + "exception_codes=0."); + return ERROR_FAIL; + } else if (!vs && !vu && !m && !s && !u) { + LOG_ERROR("Doesn't make sense to set etrigger without at " + "least one of vs, vu, m, s, or u."); + return ERROR_FAIL; + } + int result = maybe_add_trigger_t5(target, vs, vu, m, s, u, exception_codes, ETRIGGER_UNIQUE_ID); + if (result != ERROR_OK) + LOG_TARGET_ERROR(target, "Failed to set requested etrigger."); + return result; + + } else if (!strcmp(CMD_ARGV[0], "clear")) { + return riscv_clear_trigger(CMD, ETRIGGER_UNIQUE_ID, "etrigger"); + + } else { + LOG_ERROR("First argument must be either 'set' or 'clear'."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + return ERROR_OK; +} + +COMMAND_HANDLER(handle_repeat_read) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC < 2 || CMD_ARGC > 3) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t count; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], count); + target_addr_t address; + COMMAND_PARSE_ADDRESS(CMD_ARGV[1], address); + uint32_t size = 4; + if (CMD_ARGC > 2) + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], size); + + if (count == 0) + return ERROR_OK; + + uint8_t *buffer = malloc(size * count); + if (!buffer) { + LOG_ERROR("malloc failed"); + return ERROR_FAIL; + } + const struct riscv_mem_access_args args = { + .address = address, + .read_buffer = buffer, + .size = size, + .count = count, + .increment = 0, + }; + int result = r->access_memory(target, args); + if (result == ERROR_OK) + target_handle_md_output(cmd, target, address, size, count, buffer); + free(buffer); + return result; +} + +COMMAND_HANDLER(handle_memory_sample_command) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 0) { + command_print(CMD, "Memory sample configuration for %s:", target_name(target)); + for (unsigned int i = 0; i < ARRAY_SIZE(r->sample_config.bucket); i++) { + if (r->sample_config.bucket[i].enabled) { + command_print(CMD, "bucket %d; address=0x%" TARGET_PRIxADDR "; size=%d", i, + r->sample_config.bucket[i].address, + r->sample_config.bucket[i].size_bytes); + } else { + command_print(CMD, "bucket %d; disabled", i); + } + } + return ERROR_OK; + } + + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t bucket; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], bucket); + if (bucket > ARRAY_SIZE(r->sample_config.bucket)) { + LOG_TARGET_ERROR(target, "Max bucket number is %zd.", ARRAY_SIZE(r->sample_config.bucket)); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + if (!strcmp(CMD_ARGV[1], "clear")) { + r->sample_config.bucket[bucket].enabled = false; + } else { + COMMAND_PARSE_ADDRESS(CMD_ARGV[1], r->sample_config.bucket[bucket].address); + + if (CMD_ARGC > 2) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], r->sample_config.bucket[bucket].size_bytes); + if (r->sample_config.bucket[bucket].size_bytes != 4 && + r->sample_config.bucket[bucket].size_bytes != 8) { + LOG_TARGET_ERROR(target, "Only 4-byte and 8-byte sizes are supported."); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + } else { + r->sample_config.bucket[bucket].size_bytes = 4; + } + + r->sample_config.bucket[bucket].enabled = true; + } + + if (!r->sample_buf.buf) { + r->sample_buf.size = 1024 * 1024; + r->sample_buf.buf = malloc(r->sample_buf.size); + } + + /* Clear the buffer when the configuration is changed. */ + r->sample_buf.used = 0; + + r->sample_config.enabled = true; + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_dump_sample_buf_command) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + bool base64 = false; + if (CMD_ARGC > 0) { + if (!strcmp(CMD_ARGV[0], "base64")) { + base64 = true; + } else { + LOG_ERROR("Unknown argument: %s", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + + int result = ERROR_OK; + if (base64) { + unsigned char *encoded = base64_encode(r->sample_buf.buf, + r->sample_buf.used, NULL); + if (!encoded) { + LOG_TARGET_ERROR(target, "Failed base64 encode!"); + result = ERROR_FAIL; + goto error; + } + command_print(CMD, "%s", encoded); + free(encoded); + } else { + unsigned int i = 0; + while (i < r->sample_buf.used) { + uint8_t command = r->sample_buf.buf[i++]; + if (command == RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE) { + uint32_t timestamp = buf_get_u32(r->sample_buf.buf + i, 0, 32); + i += 4; + command_print(CMD, "timestamp before: %u", timestamp); + } else if (command == RISCV_SAMPLE_BUF_TIMESTAMP_AFTER) { + uint32_t timestamp = buf_get_u32(r->sample_buf.buf + i, 0, 32); + i += 4; + command_print(CMD, "timestamp after: %u", timestamp); + } else if (command < ARRAY_SIZE(r->sample_config.bucket)) { + command_print_sameline(CMD, "0x%" TARGET_PRIxADDR ": ", + r->sample_config.bucket[command].address); + if (r->sample_config.bucket[command].size_bytes == 4) { + uint32_t value = buf_get_u32(r->sample_buf.buf + i, 0, 32); + i += 4; + command_print(CMD, "0x%08" PRIx32, value); + } else if (r->sample_config.bucket[command].size_bytes == 8) { + uint64_t value = buf_get_u64(r->sample_buf.buf + i, 0, 64); + i += 8; + command_print(CMD, "0x%016" PRIx64, value); + } else { + LOG_TARGET_ERROR(target, "Found invalid size in bucket %d: %d", command, + r->sample_config.bucket[command].size_bytes); + result = ERROR_FAIL; + goto error; + } + } else { + LOG_TARGET_ERROR(target, "Found invalid command byte in sample buf: 0x%2x at offset 0x%x", + command, i - 1); + result = ERROR_FAIL; + goto error; + } + } + } + +error: + /* Clear the sample buffer even when there was an error. */ + r->sample_buf.used = 0; + return result; +} + +static COMMAND_HELPER(riscv_print_info_line_if_available, const char *section, + const char *key, unsigned int value, bool is_available) +{ + char full_key[80]; + snprintf(full_key, sizeof(full_key), "%s.%s", section, key); + if (is_available) + command_print(CMD, "%-21s %3d", full_key, value); + else + command_print(CMD, "%-21s unavailable", full_key); + return 0; +} + 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; + return CALL_COMMAND_HANDLER(riscv_print_info_line_if_available, section, + key, value, /*is_available*/ true); } COMMAND_HANDLER(handle_info) @@ -2849,37 +5397,234 @@ COMMAND_HANDLER(handle_info) /* 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); + const bool trigger_count_available = + riscv_enumerate_triggers(target) == ERROR_OK; + riscv_print_info_line_if_available(CMD, "hart", "trigger_count", + r->trigger_count, trigger_count_available); if (r->print_info) return CALL_COMMAND_HANDLER(r->print_info, target); return 0; } +COMMAND_HANDLER(riscv_exec_progbuf) +{ + if (CMD_ARGC < 1 || CMD_ARGC > 16) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct target *target = get_current_target(CMD_CTX); + + RISCV_INFO(r); + if (r->dtm_version != DTM_DTMCS_VERSION_1_0) { + LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer is " + "only supported on v0.13 or v1.0 targets."); + return ERROR_FAIL; + } + + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "exec_progbuf: Can't execute " + "program buffer, target not halted."); + return ERROR_FAIL; + } + + if (riscv_progbuf_size(target) == 0) { + LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer not implemented " + "in the target."); + return ERROR_FAIL; + } + + struct riscv_program prog; + riscv_program_init(&prog, target); + + for (unsigned int i = 0; i < CMD_ARGC; i++) { + riscv_insn_t instr; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[i], instr); + if (riscv_program_insert(&prog, instr) != ERROR_OK) + return ERROR_FAIL; + } + + if (riscv_reg_flush_all(target) != ERROR_OK) + return ERROR_FAIL; + int error = riscv_program_exec(&prog, target); + riscv_reg_cache_invalidate_all(target); + + if (error != ERROR_OK) { + LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer execution failed."); + return ERROR_FAIL; + } + + LOG_TARGET_DEBUG(target, "exec_progbuf: Program buffer execution successful."); + + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_set_enable_trigger_feature) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC == 2) { + bool enable_for_wp = true; + + if (!strcmp(CMD_ARGV[1], "wp")) + enable_for_wp = true; + else if (!strcmp(CMD_ARGV[1], "none")) + enable_for_wp = false; + else + return ERROR_COMMAND_SYNTAX_ERROR; + + if (!strcmp(CMD_ARGV[0], "all")) { + r->wp_allow_equality_match_trigger = enable_for_wp; + r->wp_allow_napot_trigger = enable_for_wp; + r->wp_allow_ge_lt_trigger = enable_for_wp; + } else if (!strcmp(CMD_ARGV[0], "eq")) { + r->wp_allow_equality_match_trigger = enable_for_wp; + } else if (!strcmp(CMD_ARGV[0], "napot")) { + r->wp_allow_napot_trigger = enable_for_wp; + } else if (!strcmp(CMD_ARGV[0], "ge_lt")) { + r->wp_allow_ge_lt_trigger = enable_for_wp; + } else { + return ERROR_COMMAND_SYNTAX_ERROR; + } + } else if (CMD_ARGC != 0) { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + command_print(CMD, "Triggers feature configuration:\n" + "Equality match trigger: for wp (%s)\n" + "NAPOT trigger: for wp (%s)\n" + "ge-lt chained triggers: for wp (%s)", + r->wp_allow_equality_match_trigger ? "enabled" : "disabled", + r->wp_allow_napot_trigger ? "enabled" : "disabled", + r->wp_allow_ge_lt_trigger ? "enabled" : "disabled"); + + return ERROR_OK; +} + +static COMMAND_HELPER(report_reserved_triggers, struct target *target) +{ + RISCV_INFO(r); + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + const char *separator = ""; + for (riscv_reg_t t = 0; t < r->trigger_count; ++t) { + if (r->reserved_triggers[t]) { + command_print_sameline(CMD, "%s%" PRIu64, separator, t); + separator = " "; + } + } + command_print_sameline(CMD, "\n"); + return ERROR_OK; +} + +COMMAND_HANDLER(handle_reserve_trigger) +{ + struct target *target = get_current_target(CMD_CTX); + if (CMD_ARGC == 0) + return CALL_COMMAND_HANDLER(report_reserved_triggers, target); + + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + riscv_reg_t t; + COMMAND_PARSE_NUMBER(u64, CMD_ARGV[0], t); + + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + RISCV_INFO(r); + if (r->trigger_count == 0) { + command_print(CMD, "Error: There are no triggers on the target."); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + if (t >= r->trigger_count) { + command_print(CMD, "Error: trigger with index %" PRIu64 + " does not exist. There are only %u triggers" + " on the target (with indexes 0 .. %u).", + t, r->trigger_count, r->trigger_count - 1); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + if (r->trigger_unique_id[t] != -1) { + command_print(CMD, "Error: trigger with index %" PRIu64 + " is already in use and can not be reserved.", t); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[1], r->reserved_triggers[t]); + return ERROR_OK; +} + +COMMAND_HANDLER(handle_riscv_virt2phys_mode) +{ + struct riscv_info *info = riscv_info(get_current_target(CMD_CTX)); + if (CMD_ARGC == 0) { + enum riscv_virt2phys_mode mode = info->virt2phys_mode; + command_print(CMD, "%s", riscv_virt2phys_mode_to_str(mode)); + return ERROR_OK; + } + + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + // TODO: add auto mode to allow OpenOCD choose translation mode + if (!strcmp(CMD_ARGV[0], + riscv_virt2phys_mode_to_str(RISCV_VIRT2PHYS_MODE_SW))) { + info->virt2phys_mode = RISCV_VIRT2PHYS_MODE_SW; + } else if (!strcmp(CMD_ARGV[0], + riscv_virt2phys_mode_to_str(RISCV_VIRT2PHYS_MODE_HW))) { + info->virt2phys_mode = RISCV_VIRT2PHYS_MODE_HW; + } else if (!strcmp(CMD_ARGV[0], + riscv_virt2phys_mode_to_str(RISCV_VIRT2PHYS_MODE_OFF))) { + info->virt2phys_mode = RISCV_VIRT2PHYS_MODE_OFF; + } else { + command_print(CMD, "Unsupported address translation mode: %s", CMD_ARGV[0]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + return ERROR_OK; +} + static const struct command_registration riscv_exec_command_handlers[] = { + { + .name = "dump_sample_buf", + .handler = handle_dump_sample_buf_command, + .mode = COMMAND_ANY, + .usage = "[base64]", + .help = "Print the contents of the sample buffer, and clear the buffer." + }, { .name = "info", .handler = handle_info, - .mode = COMMAND_EXEC, + .mode = COMMAND_ANY, .usage = "", .help = "Displays some information OpenOCD detected about the target." }, + { + .name = "memory_sample", + .handler = handle_memory_sample_command, + .mode = COMMAND_ANY, + .usage = "bucket address|clear [size=4]", + .help = "Causes OpenOCD to frequently read size bytes at the given address." + }, + { + .name = "repeat_read", + .handler = handle_repeat_read, + .mode = COMMAND_ANY, + .usage = "count address [size=4]", + .help = "Repeatedly read the value at address." + }, { .name = "set_command_timeout_sec", .handler = riscv_set_command_timeout_sec, .mode = COMMAND_ANY, - .usage = "[sec]", + .usage = "sec", .help = "Set the wall-clock timeout (in seconds) for individual commands" }, { .name = "set_reset_timeout_sec", .handler = riscv_set_reset_timeout_sec, .mode = COMMAND_ANY, - .usage = "[sec]", - .help = "Set the wall-clock timeout (in seconds) after reset is deasserted" + .usage = "sec", + .help = "DEPRECATED. Use 'riscv set_command_timeout_sec' instead." }, { .name = "set_mem_access", @@ -2889,20 +5634,11 @@ static const struct command_registration riscv_exec_command_handlers[] = { .help = "Set which memory access methods shall be used and in which order " "of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'." }, - { - .name = "set_enable_virtual", - .handler = riscv_set_enable_virtual, - .mode = COMMAND_ANY, - .usage = "on|off", - .help = "When on, memory accesses are performed on physical or virtual " - "memory depending on the current system configuration. " - "When off (default), all memory accessses are performed on physical memory." - }, { .name = "expose_csrs", .handler = riscv_set_expose_csrs, .mode = COMMAND_CONFIG, - .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...[,n15[-m15|=name15]]", .help = "Configure a list of inclusive ranges for CSRs to expose in " "addition to the standard ones. This must be executed before " "`init`." @@ -2911,11 +5647,21 @@ static const struct command_registration riscv_exec_command_handlers[] = { .name = "expose_custom", .handler = riscv_set_expose_custom, .mode = COMMAND_CONFIG, - .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...[,n15[-m15|=name15]]", .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 = "hide_csrs", + .handler = riscv_hide_csrs, + .mode = COMMAND_CONFIG, + .usage = "{n0|n-m0}[,n1|n-m1]......", + .help = "Configure a list of inclusive ranges for CSRs to hide from gdb. " + "Hidden registers are still available, but are not listed in " + "gdb target description and `reg` command output. " + "This must be executed before `init`." + }, { .name = "authdata_read", .handler = riscv_authdata_read, @@ -2934,17 +5680,34 @@ static const struct command_registration riscv_exec_command_handlers[] = { }, { .name = "dmi_read", - .handler = riscv_dmi_read, + .handler = handle_riscv_dmi_read, .mode = COMMAND_ANY, .usage = "address", - .help = "Perform a 32-bit DMI read at address, returning the value." + .help = "Read and return 32-bit value from the given address on the " + "RISC-V DMI bus." }, { .name = "dmi_write", - .handler = riscv_dmi_write, + .handler = handle_riscv_dmi_write, .mode = COMMAND_ANY, .usage = "address value", - .help = "Perform a 32-bit DMI write of value at address." + .help = "Write a 32-bit value to the given address on the RISC-V DMI bus." + }, + { + .name = "dm_read", + .handler = handle_riscv_dm_read, + .mode = COMMAND_ANY, + .usage = "reg_address", + .help = "Read and return 32-bit value from a debug module's register " + "at reg_address." + }, + { + .name = "dm_write", + .handler = handle_riscv_dm_write, + .mode = COMMAND_ANY, + .usage = "reg_address value", + .help = "Write a 32-bit value to the debug module's register at " + "reg_address." }, { .name = "reset_delays", @@ -2969,51 +5732,118 @@ static const struct command_registration riscv_exec_command_handlers[] = { .name = "set_ir", .handler = riscv_set_ir, .mode = COMMAND_ANY, - .usage = "[idcode|dtmcs|dmi] value", + .usage = "idcode|dtmcs|dmi value", .help = "Set IR value for specified JTAG register." }, { .name = "use_bscan_tunnel", .handler = riscv_use_bscan_tunnel, - .mode = COMMAND_ANY, + .mode = COMMAND_CONFIG, .usage = "value [type]", - .help = "Enable or disable use of a BSCAN tunnel to reach DM. Supply " - "the width of the DM transport TAP's instruction register to " - "enable. Supply a value of 0 to disable. Pass A second argument " - "(optional) to indicate Bscan Tunnel Type {0:(default) NESTED_TAP , " - "1: DATA_REGISTER}" + .help = "Enable or disable use of a BSCAN tunnel to reach DM." }, { - .name = "set_enable_virt2phys", - .handler = riscv_set_enable_virt2phys, - .mode = COMMAND_ANY, - .usage = "on|off", - .help = "When on (default), enable translation from virtual address to " - "physical address." + .name = "set_bscan_tunnel_ir", + .handler = riscv_set_bscan_tunnel_ir, + .mode = COMMAND_CONFIG, + .usage = "[value]", + .help = "Specify the JTAG TAP IR used to access the bscan tunnel. " + "By default it is 0x23 << (ir_length - 6), which map some " + "Xilinx FPGA (IR USER4)" + }, + { + .name = "set_maskisr", + .handler = riscv_set_maskisr, + .mode = COMMAND_EXEC, + .help = "mask riscv interrupts", + .usage = "['off'|'steponly']", }, { .name = "set_ebreakm", .handler = riscv_set_ebreakm, .mode = COMMAND_ANY, - .usage = "on|off", - .help = "Control dcsr.ebreakm. When off, M-mode ebreak instructions " - "don't trap to OpenOCD. Defaults to on." + .usage = "[on|off]", + .help = "DEPRECATED! use ' configure -ebreak' or " + "' cget -ebreak'" }, { .name = "set_ebreaks", .handler = riscv_set_ebreaks, .mode = COMMAND_ANY, - .usage = "on|off", - .help = "Control dcsr.ebreaks. When off, S-mode ebreak instructions " - "don't trap to OpenOCD. Defaults to on." + .usage = "[on|off]", + .help = "DEPRECATED! use ' configure -ebreak' or " + "' cget -ebreak'" }, { .name = "set_ebreaku", .handler = riscv_set_ebreaku, .mode = COMMAND_ANY, - .usage = "on|off", - .help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions " - "don't trap to OpenOCD. Defaults to on." + .usage = "[on|off]", + .help = "DEPRECATED! use ' configure -ebreak' or " + "' cget -ebreak'" + }, + { + .name = "etrigger", + .handler = riscv_etrigger, + .mode = COMMAND_EXEC, + .usage = "set [vs] [vu] [m] [s] [u] |clear", + .help = "Set or clear a single exception trigger." + }, + { + .name = "icount", + .handler = riscv_icount, + .mode = COMMAND_EXEC, + .usage = "set [vs] [vu] [m] [s] [u] [pending] |clear", + .help = "Set or clear a single instruction count trigger." + }, + { + .name = "itrigger", + .handler = riscv_itrigger, + .mode = COMMAND_EXEC, + .usage = "set [vs] [vu] [nmi] [m] [s] [u] |clear", + .help = "Set or clear a single interrupt trigger." + }, + { + .name = "exec_progbuf", + .handler = riscv_exec_progbuf, + .mode = COMMAND_EXEC, + .usage = "instr1 [instr2 [... instr16]]", + .help = "Execute a sequence of 32-bit instructions using the program buffer. " + "The final ebreak instruction is added automatically, if needed." + }, + { + .name = "set_enable_trigger_feature", + .handler = riscv_set_enable_trigger_feature, + .mode = COMMAND_ANY, + .usage = "[('eq'|'napot'|'ge_lt'|'all') ('wp'|'none')]", + .help = "Control whether OpenOCD is allowed to use certain RISC-V trigger features for watchpoints." + }, + { + .name = "reserve_trigger", + .handler = handle_reserve_trigger, + /* TODO: Move this to COMMAND_ANY */ + .mode = COMMAND_EXEC, + .usage = "[index ('on'|'off')]", + .help = "Controls which RISC-V triggers shall not be touched by OpenOCD.", + }, + { + .name = "virt2phys_mode", + .handler = handle_riscv_virt2phys_mode, + .mode = COMMAND_ANY, + .usage = "['sw'|'hw'|'off']", + .help = "Configure the virtual address translation mode: " + "sw - translate vaddr to paddr by manually traversing page tables, " + "hw - translate vaddr to paddr by hardware, " + "off - no address translation." + }, + { + .name = "autofence", + .handler = riscv_set_autofence, + .mode = COMMAND_ANY, + .usage = "[on|off]", + .help = "When on (default), OpenOCD will automatically execute fence instructions in some situations. " + "When off, users need to take care of memory coherency themselves, for example by using " + "`riscv exec_progbuf` to execute fence or CMO instructions." }, COMMAND_REGISTRATION_DONE }; @@ -3070,6 +5900,7 @@ struct target_type riscv_target = { .name = "riscv", .target_create = riscv_create_target, + .target_jim_configure = riscv_jim_configure, .init_target = riscv_init_target, .deinit_target = riscv_deinit_target, .examine = riscv_examine, @@ -3124,72 +5955,103 @@ static void riscv_info_init(struct target *target, struct riscv_info *r) r->common_magic = RISCV_COMMON_MAGIC; - r->dtm_version = 1; - r->current_hartid = target->coreid; + r->dtm_version = DTM_DTMCS_VERSION_UNKNOWN; r->version_specific = NULL; memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id)); r->xlen = -1; + r->virt2phys_mode = RISCV_VIRT2PHYS_MODE_SW; + + r->isrmask_mode = RISCV_ISRMASK_OFF; + 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; + r->num_enabled_mem_access_methods = RISCV_MEM_ACCESS_MAX_METHODS_NUM; + for (size_t i = 0; i < RISCV_MEM_ACCESS_MAX_METHODS_NUM; ++i) + r->mem_access_warn[i] = true; INIT_LIST_HEAD(&r->expose_csr); INIT_LIST_HEAD(&r->expose_custom); + INIT_LIST_HEAD(&r->hide_csr); + + r->vsew64_supported = YNM_MAYBE; + + r->wp_allow_equality_match_trigger = true; + r->wp_allow_ge_lt_trigger = true; + r->wp_allow_napot_trigger = true; + + r->autofence = true; } static int riscv_resume_go_all_harts(struct target *target) { RISCV_INFO(r); - LOG_DEBUG("[%s] resuming hart", target_name(target)); - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - if (riscv_is_halted(target)) { + LOG_TARGET_DEBUG(target, "Resuming hart, state=%d.", target->state); + if (target->state == TARGET_HALTED) { if (r->resume_go(target) != ERROR_OK) return ERROR_FAIL; } else { - LOG_DEBUG("[%s] hart requested resume, but was already resumed", - target_name(target)); + LOG_TARGET_DEBUG(target, "Hart requested resume, but was already resumed."); } - - riscv_invalidate_register_cache(target); return ERROR_OK; } -/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS - * then the only hart. */ +static int riscv_interrupts_disable(struct target *target, riscv_reg_t *old_mstatus) +{ + LOG_TARGET_DEBUG(target, "Disabling interrupts."); + riscv_reg_t current_mstatus; + int ret = riscv_reg_get(target, ¤t_mstatus, GDB_REGNO_MSTATUS); + if (ret != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read mstatus!"); + return ret; + } + if (old_mstatus) + *old_mstatus = current_mstatus; + return riscv_reg_set(target, GDB_REGNO_MSTATUS, current_mstatus & ~mstatus_ie_mask); +} + +static int riscv_interrupts_restore(struct target *target, riscv_reg_t old_mstatus) +{ + LOG_TARGET_DEBUG(target, "Restoring interrupts."); + riscv_reg_t current_mstatus; + int ret = riscv_reg_get(target, ¤t_mstatus, GDB_REGNO_MSTATUS); + if (ret != ERROR_OK) { + LOG_TARGET_ERROR(target, "Failed to read mstatus!"); + return ret; + } + if ((current_mstatus & mstatus_ie_mask) != 0) { + LOG_TARGET_WARNING(target, "Interrupt enable bits in mstatus changed during single-step."); + LOG_TARGET_WARNING(target, "OpenOCD might have affected the program when it restored the interrupt bits after single-step."); + LOG_TARGET_WARNING(target, "Hint: Use 'riscv set_maskisr off' to prevent OpenOCD from touching mstatus during single-step."); + } + return riscv_reg_set(target, GDB_REGNO_MSTATUS, current_mstatus | (old_mstatus & mstatus_ie_mask)); +} + static int riscv_step_rtos_hart(struct target *target) { RISCV_INFO(r); - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - LOG_DEBUG("[%s] stepping", target_name(target)); + LOG_TARGET_DEBUG(target, "Stepping."); - if (!riscv_is_halted(target)) { - LOG_ERROR("Hart isn't halted before single step!"); + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Hart isn't halted before single step!"); return ERROR_FAIL; } - riscv_invalidate_register_cache(target); r->on_step(target); if (r->step_current_hart(target) != ERROR_OK) return ERROR_FAIL; - riscv_invalidate_register_cache(target); - r->on_halt(target); - if (!riscv_is_halted(target)) { - LOG_ERROR("Hart was not halted after single step!"); + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Hart was not halted after single step!"); return ERROR_FAIL; } return ERROR_OK; } -bool riscv_supports_extension(struct target *target, char letter) +bool riscv_supports_extension(const struct target *target, char letter) { RISCV_INFO(r); unsigned int num; @@ -3208,231 +6070,147 @@ unsigned int riscv_xlen(const struct target *target) return r->xlen; } -int riscv_set_current_hartid(struct target *target, int hartid) +unsigned int riscv_vlenb(const struct target *target) { RISCV_INFO(r); - if (!r->select_current_hart) - return ERROR_OK; - - int previous_hartid = riscv_current_hartid(target); - r->current_hartid = hartid; - LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid); - if (r->select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; - - return ERROR_OK; + return r->vlenb; } -/* Invalidates the register cache. */ -static void riscv_invalidate_register_cache(struct target *target) -{ - 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; - } -} - -int riscv_current_hartid(const struct target *target) +int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state) { RISCV_INFO(r); - return r->current_hartid; + assert(r->get_hart_state); + return r->get_hart_state(target, state); } -int riscv_count_harts(struct target *target) -{ - if (!target) - return 1; - RISCV_INFO(r); - if (!r || !r->hart_count) - return 1; - return r->hart_count(target); -} - -/** - * If write is true: - * return true iff we are guaranteed that the register will contain exactly - * the value we just wrote when it's read. - * If write is false: - * return true iff we are guaranteed that the register will read the same - * value in the future as the value we just read. - */ -static bool gdb_regno_cacheable(enum gdb_regno regno, bool write) -{ - /* GPRs, FPRs, vector registers are just normal data stores. */ - if (regno <= GDB_REGNO_XPR31 || - (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) || - (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)) - return true; - - /* Most CSRs won't change value on us, but we can't assume it about arbitrary - * CSRs. */ - switch (regno) { - case GDB_REGNO_DPC: - return true; - - case GDB_REGNO_VSTART: - case GDB_REGNO_VXSAT: - case GDB_REGNO_VXRM: - case GDB_REGNO_VLENB: - case GDB_REGNO_VL: - case GDB_REGNO_VTYPE: - case GDB_REGNO_MISA: - case GDB_REGNO_DCSR: - case GDB_REGNO_DSCRATCH0: - case GDB_REGNO_MSTATUS: - case GDB_REGNO_MEPC: - case GDB_REGNO_MCAUSE: - case GDB_REGNO_SATP: - /* - * WARL registers might not contain the value we just wrote, but - * these ones won't spontaneously change their value either. * - */ - return !write; - - case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */ - case GDB_REGNO_TDATA1: /* Changes value when tselect is changed. */ - case GDB_REGNO_TDATA2: /* Changse value when tselect is changed. */ - default: - return false; - } -} - -/** - * This function is called when the debug user wants to change the value of a - * register. The new value may be cached, and may not be written until the hart - * is resumed. */ -int riscv_set_register(struct target *target, enum gdb_regno regid, riscv_reg_t value) +static enum riscv_halt_reason riscv_halt_reason(struct target *target) { RISCV_INFO(r); - 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, 'E')) - return ERROR_OK; - - struct reg *reg = &target->reg_cache->reg_list[regid]; - buf_set_u64(reg->value, 0, reg->size, value); - - int result = r->set_register(target, regid, value); - if (result == ERROR_OK) - reg->valid = gdb_regno_cacheable(regid, true); - else - reg->valid = false; - LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d", - target_name(target), value, reg->name, reg->valid); - return result; -} - -int riscv_get_register(struct target *target, riscv_reg_t *value, - enum gdb_regno regid) -{ - RISCV_INFO(r); - - keep_alive(); - - struct reg *reg = &target->reg_cache->reg_list[regid]; - if (!reg->exist) { - LOG_DEBUG("[%s] %s does not exist.", - target_name(target), gdb_regno_name(regid)); - return ERROR_FAIL; - } - - if (reg && reg->valid) { - *value = buf_get_u64(reg->value, 0, reg->size); - LOG_DEBUG("[%s] %s: %" PRIx64 " (cached)", target_name(target), - gdb_regno_name(regid), *value); - return ERROR_OK; - } - - /* TODO: Hack to deal with gdb that thinks these registers still exist. */ - if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && - riscv_supports_extension(target, 'E')) { - *value = 0; - return ERROR_OK; - } - - int result = r->get_register(target, value, regid); - - if (result == ERROR_OK) - reg->valid = gdb_regno_cacheable(regid, false); - - LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target), - gdb_regno_name(regid), *value); - return result; -} - -bool riscv_is_halted(struct target *target) -{ - RISCV_INFO(r); - assert(r->is_halted); - return r->is_halted(target); -} - -static enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid) -{ - RISCV_INFO(r); - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) - return RISCV_HALT_ERROR; - if (!riscv_is_halted(target)) { - LOG_ERROR("Hart is not halted!"); + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Hart is not halted!"); return RISCV_HALT_UNKNOWN; } return r->halt_reason(target); } -size_t riscv_debug_buffer_size(struct target *target) +unsigned int riscv_progbuf_size(struct target *target) { RISCV_INFO(r); - return r->debug_buffer_size; + return r->get_progbufsize(target); } -int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn) +int riscv_write_progbuf(struct target *target, unsigned int index, riscv_insn_t insn) { RISCV_INFO(r); - r->write_debug_buffer(target, index, insn); + return r->write_progbuf(target, index, insn); +} + +riscv_insn_t riscv_read_progbuf(struct target *target, int index) +{ + RISCV_INFO(r); + return r->read_progbuf(target, index); +} + +int riscv_execute_progbuf(struct target *target, uint32_t *cmderr) +{ + RISCV_INFO(r); + return r->execute_progbuf(target, cmderr); +} + +void riscv_fill_dmi_write(const struct target *target, uint8_t *buf, uint32_t a, uint32_t d) +{ + RISCV_INFO(r); + r->fill_dmi_write(target, buf, a, d); +} + +void riscv_fill_dmi_read(const struct target *target, uint8_t *buf, uint32_t a) +{ + RISCV_INFO(r); + r->fill_dmi_read(target, buf, a); +} + +void riscv_fill_dm_nop(const struct target *target, uint8_t *buf) +{ + RISCV_INFO(r); + r->fill_dm_nop(target, buf); +} + +unsigned int riscv_get_dmi_address_bits(const struct target *target) +{ + RISCV_INFO(r); + return r->get_dmi_address_bits(target); +} + +static int check_if_trigger_exists(struct target *target, unsigned int index) +{ + /* If we can't write tselect, then this hart does not support triggers. */ + if (riscv_reg_set(target, GDB_REGNO_TSELECT, index) != ERROR_OK) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + riscv_reg_t tselect_rb; + if (riscv_reg_get(target, &tselect_rb, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; + /* Mask off the top bit, which is used as tdrmode in legacy RISC-V Debug Spec + * (old revisions of v0.11 spec). */ + tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1)); + if (tselect_rb != index) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; return ERROR_OK; } -riscv_insn_t riscv_read_debug_buffer(struct target *target, int index) +/** + * This function reads `tinfo` or `tdata1`, when reading `tinfo` fails, + * to determine trigger types supported by a trigger. + * It is assumed that the trigger is already selected via writing `tselect`. + */ +static int get_trigger_types(struct target *target, unsigned int *trigger_tinfo, + riscv_reg_t tdata1) { - RISCV_INFO(r); - return r->read_debug_buffer(target, index); + assert(trigger_tinfo); + riscv_reg_t tinfo; + if (riscv_reg_get(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK) { + /* tinfo.INFO == 1: trigger doesn’t exist + * tinfo == 0 or tinfo.INFO != 1 and tinfo LSB is set: invalid tinfo */ + if (tinfo == 0 || tinfo & 0x1) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + *trigger_tinfo = tinfo; + return ERROR_OK; + } + const unsigned int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target))); + if (type == 0) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + *trigger_tinfo = 1 << type; + return ERROR_OK; } -int riscv_execute_debug_buffer(struct target *target) +static int disable_trigger_if_dmode(struct target *target, riscv_reg_t tdata1) { - RISCV_INFO(r); - return r->execute_debug_buffer(target); -} - -void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d) -{ - RISCV_INFO(r); - r->fill_dmi_write_u64(target, buf, a, d); -} - -void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a) -{ - RISCV_INFO(r); - r->fill_dmi_read_u64(target, buf, a); -} - -void riscv_fill_dmi_nop_u64(struct target *target, char *buf) -{ - RISCV_INFO(r); - r->fill_dmi_nop_u64(target, buf); -} - -int riscv_dmi_write_u64_bits(struct target *target) -{ - RISCV_INFO(r); - return r->dmi_write_u64_bits(target); + bool dmode_is_set = false; + switch (get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)))) { + case CSR_TDATA1_TYPE_LEGACY: + /* On these older cores we don't support software using + * triggers. */ + dmode_is_set = true; + break; + case CSR_TDATA1_TYPE_MCONTROL: + dmode_is_set = tdata1 & CSR_MCONTROL_DMODE(riscv_xlen(target)); + break; + case CSR_TDATA1_TYPE_MCONTROL6: + dmode_is_set = tdata1 & CSR_MCONTROL6_DMODE(riscv_xlen(target)); + break; + case CSR_TDATA1_TYPE_ICOUNT: + dmode_is_set = tdata1 & CSR_ICOUNT_DMODE(riscv_xlen(target)); + break; + case CSR_TDATA1_TYPE_ITRIGGER: + dmode_is_set = tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target)); + break; + case CSR_TDATA1_TYPE_ETRIGGER: + dmode_is_set = tdata1 & CSR_ETRIGGER_DMODE(riscv_xlen(target)); + break; + } + if (!dmode_is_set) + /* Nothing to do */ + return ERROR_OK; + return riscv_reg_set(target, GDB_REGNO_TDATA1, 0); } /** @@ -3441,7 +6219,7 @@ int riscv_dmi_write_u64_bits(struct target *target) * something. * Disable any hardware triggers that have dmode set. We can't have set them * ourselves. Maybe they're left over from some killed debug session. - * */ + */ int riscv_enumerate_triggers(struct target *target) { RISCV_INFO(r); @@ -3449,974 +6227,79 @@ int riscv_enumerate_triggers(struct target *target) if (r->triggers_enumerated) return ERROR_OK; - r->triggers_enumerated = true; /* At the very least we tried. */ + if (target->state != TARGET_HALTED) { + LOG_TARGET_ERROR(target, "Unable to enumerate triggers: target not halted."); + return ERROR_FAIL; + } - riscv_reg_t tselect; - int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT); + riscv_reg_t orig_tselect; + int result = riscv_reg_get(target, &orig_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. */ + * implemented. */ if (result != ERROR_OK) { - LOG_DEBUG("[%s] Cannot access tselect register. " - "Assuming that triggers are not implemented.", target_name(target)); + LOG_TARGET_INFO(target, "Cannot access tselect register. " + "Assuming that triggers are not implemented."); + r->triggers_enumerated = true; r->trigger_count = 0; + free(r->reserved_triggers); + r->reserved_triggers = NULL; return ERROR_OK; } - 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; - - int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); - if (type == 0) - break; - switch (type) { - case 1: - /* On these older cores we don't support software using - * triggers. */ - riscv_set_register(target, GDB_REGNO_TDATA1, 0); - break; - case 2: - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) - riscv_set_register(target, GDB_REGNO_TDATA1, 0); - break; - case 6: - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) - riscv_set_register(target, GDB_REGNO_TDATA1, 0); - break; - } - } - - riscv_set_register(target, GDB_REGNO_TSELECT, tselect); - - LOG_INFO("[%s] Found %d triggers", target_name(target), r->trigger_count); - - return ERROR_OK; -} - -const char *gdb_regno_name(enum gdb_regno regno) -{ - static char buf[32]; - - switch (regno) { - case GDB_REGNO_ZERO: - return "zero"; - case GDB_REGNO_RA: - return "ra"; - case GDB_REGNO_SP: - return "sp"; - case GDB_REGNO_GP: - return "gp"; - case GDB_REGNO_TP: - return "tp"; - case GDB_REGNO_T0: - return "t0"; - case GDB_REGNO_T1: - return "t1"; - case GDB_REGNO_T2: - return "t2"; - case GDB_REGNO_S0: - return "s0"; - case GDB_REGNO_S1: - return "s1"; - case GDB_REGNO_A0: - return "a0"; - case GDB_REGNO_A1: - return "a1"; - case GDB_REGNO_A2: - return "a2"; - case GDB_REGNO_A3: - return "a3"; - case GDB_REGNO_A4: - return "a4"; - case GDB_REGNO_A5: - return "a5"; - case GDB_REGNO_A6: - return "a6"; - case GDB_REGNO_A7: - return "a7"; - case GDB_REGNO_S2: - return "s2"; - case GDB_REGNO_S3: - return "s3"; - case GDB_REGNO_S4: - return "s4"; - case GDB_REGNO_S5: - return "s5"; - case GDB_REGNO_S6: - return "s6"; - case GDB_REGNO_S7: - return "s7"; - case GDB_REGNO_S8: - return "s8"; - case GDB_REGNO_S9: - return "s9"; - case GDB_REGNO_S10: - return "s10"; - case GDB_REGNO_S11: - return "s11"; - case GDB_REGNO_T3: - return "t3"; - case GDB_REGNO_T4: - return "t4"; - case GDB_REGNO_T5: - return "t5"; - case GDB_REGNO_T6: - return "t6"; - case GDB_REGNO_PC: - return "pc"; - case GDB_REGNO_FPR0: - return "fpr0"; - case GDB_REGNO_FPR31: - return "fpr31"; - case GDB_REGNO_CSR0: - return "csr0"; - case GDB_REGNO_TSELECT: - return "tselect"; - case GDB_REGNO_TDATA1: - return "tdata1"; - case GDB_REGNO_TDATA2: - return "tdata2"; - case GDB_REGNO_MISA: - return "misa"; - case GDB_REGNO_DPC: - return "dpc"; - case GDB_REGNO_DCSR: - return "dcsr"; - case GDB_REGNO_DSCRATCH0: - return "dscratch0"; - case GDB_REGNO_MSTATUS: - return "mstatus"; - case GDB_REGNO_MEPC: - return "mepc"; - case GDB_REGNO_MCAUSE: - return "mcause"; - case GDB_REGNO_PRIV: - return "priv"; - case GDB_REGNO_SATP: - return "satp"; - case GDB_REGNO_VTYPE: - return "vtype"; - case GDB_REGNO_VL: - return "vl"; - case GDB_REGNO_V0: - return "v0"; - case GDB_REGNO_V1: - return "v1"; - case GDB_REGNO_V2: - return "v2"; - case GDB_REGNO_V3: - return "v3"; - case GDB_REGNO_V4: - return "v4"; - case GDB_REGNO_V5: - return "v5"; - case GDB_REGNO_V6: - return "v6"; - case GDB_REGNO_V7: - return "v7"; - case GDB_REGNO_V8: - return "v8"; - case GDB_REGNO_V9: - return "v9"; - case GDB_REGNO_V10: - return "v10"; - case GDB_REGNO_V11: - return "v11"; - case GDB_REGNO_V12: - return "v12"; - case GDB_REGNO_V13: - return "v13"; - case GDB_REGNO_V14: - return "v14"; - case GDB_REGNO_V15: - return "v15"; - case GDB_REGNO_V16: - return "v16"; - case GDB_REGNO_V17: - return "v17"; - case GDB_REGNO_V18: - return "v18"; - case GDB_REGNO_V19: - return "v19"; - case GDB_REGNO_V20: - return "v20"; - case GDB_REGNO_V21: - return "v21"; - case GDB_REGNO_V22: - return "v22"; - case GDB_REGNO_V23: - return "v23"; - case GDB_REGNO_V24: - return "v24"; - case GDB_REGNO_V25: - return "v25"; - case GDB_REGNO_V26: - return "v26"; - case GDB_REGNO_V27: - return "v27"; - case GDB_REGNO_V28: - return "v28"; - case GDB_REGNO_V29: - return "v29"; - case GDB_REGNO_V30: - return "v30"; - case GDB_REGNO_V31: - return "v31"; - default: - if (regno <= GDB_REGNO_XPR31) - sprintf(buf, "x%d", regno - GDB_REGNO_ZERO); - else if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) - sprintf(buf, "csr%d", regno - GDB_REGNO_CSR0); - else if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) - sprintf(buf, "f%d", regno - GDB_REGNO_FPR0); - else - sprintf(buf, "gdb_regno_%d", regno); - return buf; - } -} - -static int register_get(struct reg *reg) -{ - riscv_reg_info_t *reg_info = reg->arch_info; - struct target *target = reg_info->target; - RISCV_INFO(r); - - if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) { - if (!r->get_register_buf) { - LOG_ERROR("Reading register %s not supported on this RISC-V target.", - gdb_regno_name(reg->number)); - return ERROR_FAIL; - } - - if (r->get_register_buf(target, reg->value, reg->number) != ERROR_OK) - return ERROR_FAIL; - } else { - uint64_t value; - int result = riscv_get_register(target, &value, reg->number); - if (result != ERROR_OK) - return result; - buf_set_u64(reg->value, 0, reg->size, value); - } - reg->valid = gdb_regno_cacheable(reg->number, false); - char *str = buf_to_hex_str(reg->value, reg->size); - LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target), - str, reg->name, reg->valid); - free(str); - return ERROR_OK; -} - -static int register_set(struct reg *reg, uint8_t *buf) -{ - riscv_reg_info_t *reg_info = reg->arch_info; - struct target *target = reg_info->target; - RISCV_INFO(r); - - char *str = buf_to_hex_str(buf, reg->size); - LOG_DEBUG("[%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); - - if (reg->number == GDB_REGNO_TDATA1 || - reg->number == GDB_REGNO_TDATA2) { - r->manual_hwbp_set = true; - /* When enumerating triggers, we clear any triggers with DMODE set, - * assuming they were left over from a previous debug session. So make - * sure that is done before a user might be setting their own triggers. - */ - if (riscv_enumerate_triggers(target) != ERROR_OK) - return ERROR_FAIL; - } - - if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) { - if (!r->set_register_buf) { - LOG_ERROR("Writing register %s not supported on this RISC-V target.", - gdb_regno_name(reg->number)); - return ERROR_FAIL; - } - - if (r->set_register_buf(target, reg->number, reg->value) != ERROR_OK) - return ERROR_FAIL; - } else { - uint64_t value = buf_get_u64(buf, 0, reg->size); - if (riscv_set_register(target, reg->number, value) != ERROR_OK) - return ERROR_FAIL; - } - - return ERROR_OK; -} - -static struct reg_arch_type riscv_reg_arch_type = { - .get = register_get, - .set = register_set -}; - -struct csr_info { - unsigned int number; - const char *name; -}; - -static int cmp_csr_info(const void *p1, const void *p2) -{ - return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number); -} - -int riscv_init_registers(struct target *target) -{ - RISCV_INFO(info); - - riscv_free_registers(target); - - target->reg_cache = calloc(1, sizeof(*target->reg_cache)); - if (!target->reg_cache) - return ERROR_FAIL; - target->reg_cache->name = "RISC-V Registers"; - target->reg_cache->num_regs = GDB_REGNO_COUNT; - - if (!list_empty(&info->expose_custom)) { - range_list_t *entry; - list_for_each_entry(entry, &info->expose_custom, list) - target->reg_cache->num_regs += entry->high - entry->low + 1; - } - - LOG_DEBUG("create register cache for %d registers", - target->reg_cache->num_regs); - - target->reg_cache->reg_list = - calloc(target->reg_cache->num_regs, sizeof(struct reg)); - if (!target->reg_cache->reg_list) - return ERROR_FAIL; - - const unsigned int max_reg_name_len = 12; - free(info->reg_names); - info->reg_names = - calloc(target->reg_cache->num_regs, max_reg_name_len); - if (!info->reg_names) - return ERROR_FAIL; - char *reg_name = info->reg_names; - - static struct reg_feature feature_cpu = { - .name = "org.gnu.gdb.riscv.cpu" - }; - static struct reg_feature feature_fpu = { - .name = "org.gnu.gdb.riscv.fpu" - }; - static struct reg_feature feature_csr = { - .name = "org.gnu.gdb.riscv.csr" - }; - static struct reg_feature feature_vector = { - .name = "org.gnu.gdb.riscv.vector" - }; - static struct reg_feature feature_virtual = { - .name = "org.gnu.gdb.riscv.virtual" - }; - static struct reg_feature feature_custom = { - .name = "org.gnu.gdb.riscv.custom" - }; - - /* These types are built into gdb. */ - static struct reg_data_type type_ieee_single = { .type = REG_TYPE_IEEE_SINGLE, .id = "ieee_single" }; - static struct reg_data_type type_ieee_double = { .type = REG_TYPE_IEEE_DOUBLE, .id = "ieee_double" }; - static struct reg_data_type_union_field single_double_fields[] = { - {"float", &type_ieee_single, single_double_fields + 1}, - {"double", &type_ieee_double, NULL}, - }; - static struct reg_data_type_union single_double_union = { - .fields = single_double_fields - }; - static struct reg_data_type type_ieee_single_double = { - .type = REG_TYPE_ARCH_DEFINED, - .id = "FPU_FD", - .type_class = REG_TYPE_CLASS_UNION, - { .reg_type_union = &single_double_union } - }; - static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" }; - static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" }; - static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" }; - static struct reg_data_type type_uint64 = { .type = REG_TYPE_UINT64, .id = "uint64" }; - static struct reg_data_type type_uint128 = { .type = REG_TYPE_UINT128, .id = "uint128" }; - - /* This is roughly the XML we want: - * - * - * - * - * - * - * - * - * - * - * - * + /* Obtaining tinfo.version value once. + * No need to enumerate per-trigger. + * See https://github.com/riscv/riscv-debug-spec/pull/1081. */ - - info->vector_uint8.type = &type_uint8; - info->vector_uint8.count = info->vlenb; - info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED; - info->type_uint8_vector.id = "bytes"; - info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR; - info->type_uint8_vector.reg_type_vector = &info->vector_uint8; - - info->vector_uint16.type = &type_uint16; - info->vector_uint16.count = info->vlenb / 2; - info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED; - info->type_uint16_vector.id = "shorts"; - info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR; - info->type_uint16_vector.reg_type_vector = &info->vector_uint16; - - info->vector_uint32.type = &type_uint32; - info->vector_uint32.count = info->vlenb / 4; - info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED; - info->type_uint32_vector.id = "words"; - info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR; - info->type_uint32_vector.reg_type_vector = &info->vector_uint32; - - info->vector_uint64.type = &type_uint64; - info->vector_uint64.count = info->vlenb / 8; - info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED; - info->type_uint64_vector.id = "longs"; - info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR; - info->type_uint64_vector.reg_type_vector = &info->vector_uint64; - - info->vector_uint128.type = &type_uint128; - info->vector_uint128.count = info->vlenb / 16; - info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED; - info->type_uint128_vector.id = "quads"; - info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR; - info->type_uint128_vector.reg_type_vector = &info->vector_uint128; - - info->vector_fields[0].name = "b"; - info->vector_fields[0].type = &info->type_uint8_vector; - if (info->vlenb >= 2) { - info->vector_fields[0].next = info->vector_fields + 1; - info->vector_fields[1].name = "s"; - info->vector_fields[1].type = &info->type_uint16_vector; + riscv_reg_t tinfo; + if (riscv_reg_get(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK) { + r->tinfo_version = get_field(tinfo, CSR_TINFO_VERSION); + LOG_TARGET_DEBUG(target, "Trigger tinfo.version = %d.", r->tinfo_version); } else { - info->vector_fields[0].next = NULL; + r->tinfo_version = RISCV_TINFO_VERSION_UNKNOWN; + LOG_TARGET_DEBUG(target, "Trigger tinfo.version is unknown."); } - 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; + + unsigned int t = 0; + for (; t < ARRAY_SIZE(r->trigger_tinfo); ++t) { + result = check_if_trigger_exists(target, t); + if (result == ERROR_FAIL) + return ERROR_FAIL; + if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + break; + + riscv_reg_t tdata1; + if (riscv_reg_get(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK) + return ERROR_FAIL; + + result = get_trigger_types(target, &r->trigger_tinfo[t], tdata1); + if (result == ERROR_FAIL) + return ERROR_FAIL; + if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + break; + + LOG_TARGET_DEBUG(target, "Trigger %u: supported types (mask) = 0x%08x", + t, r->trigger_tinfo[t]); + + if (disable_trigger_if_dmode(target, tdata1) != ERROR_OK) + return ERROR_FAIL; } - if (info->vlenb >= 8) { - info->vector_fields[2].next = info->vector_fields + 3; - info->vector_fields[3].name = "l"; - info->vector_fields[3].type = &info->type_uint64_vector; - } else { - info->vector_fields[2].next = NULL; - } - if (info->vlenb >= 16) { - info->vector_fields[3].next = info->vector_fields + 4; - info->vector_fields[4].name = "q"; - info->vector_fields[4].type = &info->type_uint128_vector; - } else { - info->vector_fields[3].next = NULL; - } - info->vector_fields[4].next = NULL; - info->vector_union.fields = info->vector_fields; - - info->type_vector.type = REG_TYPE_ARCH_DEFINED; - info->type_vector.id = "riscv_vector"; - info->type_vector.type_class = REG_TYPE_CLASS_UNION; - info->type_vector.reg_type_union = &info->vector_union; - - struct csr_info csr_info[] = { -#define DECLARE_CSR(name, number) { number, #name }, -#include "encoding.h" -#undef DECLARE_CSR - }; - /* encoding.h does not contain the registers in sorted order. */ - qsort(csr_info, ARRAY_SIZE(csr_info), sizeof(*csr_info), cmp_csr_info); - unsigned int csr_info_index = 0; - - int custom_within_range = 0; - - riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t)); - if (!shared_reg_info) + if (riscv_reg_set(target, GDB_REGNO_TSELECT, orig_tselect) != ERROR_OK) return ERROR_FAIL; - shared_reg_info->target = target; - - /* When gdb requests register N, gdb_get_register_packet() assumes that this - * is register at index N in reg_list. So if there are certain registers - * that don't exist, we need to leave holes in the list (or renumber, but - * it would be nice not to have yet another set of numbers to translate - * between). */ - for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) { - struct reg *r = &target->reg_cache->reg_list[number]; - r->dirty = false; - r->valid = false; - r->exist = true; - r->type = &riscv_reg_arch_type; - r->arch_info = shared_reg_info; - r->number = number; - r->size = riscv_xlen(target); - /* r->size is set in riscv_invalidate_register_cache, maybe because the - * target is in theory allowed to change XLEN on us. But I expect a lot - * of other things to break in that case as well. */ - if (number <= GDB_REGNO_XPR31) { - r->exist = number <= GDB_REGNO_XPR15 || - !riscv_supports_extension(target, 'E'); - /* TODO: For now we fake that all GPRs exist because otherwise gdb - * doesn't work. */ - r->exist = true; - r->caller_save = true; - switch (number) { - case GDB_REGNO_ZERO: - r->name = "zero"; - break; - case GDB_REGNO_RA: - r->name = "ra"; - break; - case GDB_REGNO_SP: - r->name = "sp"; - break; - case GDB_REGNO_GP: - r->name = "gp"; - break; - case GDB_REGNO_TP: - r->name = "tp"; - break; - case GDB_REGNO_T0: - r->name = "t0"; - break; - case GDB_REGNO_T1: - r->name = "t1"; - break; - case GDB_REGNO_T2: - r->name = "t2"; - break; - case GDB_REGNO_FP: - r->name = "fp"; - break; - case GDB_REGNO_S1: - r->name = "s1"; - break; - case GDB_REGNO_A0: - r->name = "a0"; - break; - case GDB_REGNO_A1: - r->name = "a1"; - break; - case GDB_REGNO_A2: - r->name = "a2"; - break; - case GDB_REGNO_A3: - r->name = "a3"; - break; - case GDB_REGNO_A4: - r->name = "a4"; - break; - case GDB_REGNO_A5: - r->name = "a5"; - break; - case GDB_REGNO_A6: - r->name = "a6"; - break; - case GDB_REGNO_A7: - r->name = "a7"; - break; - case GDB_REGNO_S2: - r->name = "s2"; - break; - case GDB_REGNO_S3: - r->name = "s3"; - break; - case GDB_REGNO_S4: - r->name = "s4"; - break; - case GDB_REGNO_S5: - r->name = "s5"; - break; - case GDB_REGNO_S6: - r->name = "s6"; - break; - case GDB_REGNO_S7: - r->name = "s7"; - break; - case GDB_REGNO_S8: - r->name = "s8"; - break; - case GDB_REGNO_S9: - r->name = "s9"; - break; - case GDB_REGNO_S10: - r->name = "s10"; - break; - case GDB_REGNO_S11: - r->name = "s11"; - break; - case GDB_REGNO_T3: - r->name = "t3"; - break; - case GDB_REGNO_T4: - r->name = "t4"; - break; - case GDB_REGNO_T5: - r->name = "t5"; - break; - case GDB_REGNO_T6: - r->name = "t6"; - break; - } - r->group = "general"; - r->feature = &feature_cpu; - } else if (number == GDB_REGNO_PC) { - r->caller_save = true; - sprintf(reg_name, "pc"); - r->group = "general"; - r->feature = &feature_cpu; - } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - r->caller_save = true; - if (riscv_supports_extension(target, 'D')) { - r->size = 64; - if (riscv_supports_extension(target, 'F')) - r->reg_data_type = &type_ieee_single_double; - else - r->reg_data_type = &type_ieee_double; - } else if (riscv_supports_extension(target, 'F')) { - r->reg_data_type = &type_ieee_single; - r->size = 32; - } else { - r->exist = false; - } - switch (number) { - case GDB_REGNO_FT0: - r->name = "ft0"; - break; - case GDB_REGNO_FT1: - r->name = "ft1"; - break; - case GDB_REGNO_FT2: - r->name = "ft2"; - break; - case GDB_REGNO_FT3: - r->name = "ft3"; - break; - case GDB_REGNO_FT4: - r->name = "ft4"; - break; - case GDB_REGNO_FT5: - r->name = "ft5"; - break; - case GDB_REGNO_FT6: - r->name = "ft6"; - break; - case GDB_REGNO_FT7: - r->name = "ft7"; - break; - case GDB_REGNO_FS0: - r->name = "fs0"; - break; - case GDB_REGNO_FS1: - r->name = "fs1"; - break; - case GDB_REGNO_FA0: - r->name = "fa0"; - break; - case GDB_REGNO_FA1: - r->name = "fa1"; - break; - case GDB_REGNO_FA2: - r->name = "fa2"; - break; - case GDB_REGNO_FA3: - r->name = "fa3"; - break; - case GDB_REGNO_FA4: - r->name = "fa4"; - break; - case GDB_REGNO_FA5: - r->name = "fa5"; - break; - case GDB_REGNO_FA6: - r->name = "fa6"; - break; - case GDB_REGNO_FA7: - r->name = "fa7"; - break; - case GDB_REGNO_FS2: - r->name = "fs2"; - break; - case GDB_REGNO_FS3: - r->name = "fs3"; - break; - case GDB_REGNO_FS4: - r->name = "fs4"; - break; - case GDB_REGNO_FS5: - r->name = "fs5"; - break; - case GDB_REGNO_FS6: - r->name = "fs6"; - break; - case GDB_REGNO_FS7: - r->name = "fs7"; - break; - case GDB_REGNO_FS8: - r->name = "fs8"; - break; - case GDB_REGNO_FS9: - r->name = "fs9"; - break; - case GDB_REGNO_FS10: - r->name = "fs10"; - break; - case GDB_REGNO_FS11: - r->name = "fs11"; - break; - case GDB_REGNO_FT8: - r->name = "ft8"; - break; - case GDB_REGNO_FT9: - r->name = "ft9"; - break; - case GDB_REGNO_FT10: - r->name = "ft10"; - break; - case GDB_REGNO_FT11: - r->name = "ft11"; - break; - } - r->group = "float"; - r->feature = &feature_fpu; - } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - r->group = "csr"; - r->feature = &feature_csr; - unsigned int csr_number = number - GDB_REGNO_CSR0; - - while (csr_info[csr_info_index].number < csr_number && - csr_info_index < ARRAY_SIZE(csr_info) - 1) { - csr_info_index++; - } - if (csr_info[csr_info_index].number == csr_number) { - r->name = csr_info[csr_info_index].name; - } else { - sprintf(reg_name, "csr%d", csr_number); - /* Assume unnamed registers don't exist, unless we have some - * configuration that tells us otherwise. That's important - * because eg. Eclipse crashes if a target has too many - * registers, and apparently has no way of only showing a - * subset of registers in any case. */ - r->exist = false; - } - - switch (csr_number) { - case CSR_FFLAGS: - case CSR_FRM: - case CSR_FCSR: - r->exist = riscv_supports_extension(target, 'F'); - r->group = "float"; - r->feature = &feature_fpu; - break; - case CSR_SSTATUS: - case CSR_STVEC: - case CSR_SIP: - case CSR_SIE: - case CSR_SCOUNTEREN: - case CSR_SSCRATCH: - case CSR_SEPC: - case CSR_SCAUSE: - case CSR_STVAL: - case CSR_SATP: - 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, 'S') || - riscv_supports_extension(target, 'N'); - break; - - case CSR_PMPCFG1: - case CSR_PMPCFG3: - case CSR_CYCLEH: - case CSR_TIMEH: - case CSR_INSTRETH: - case CSR_HPMCOUNTER3H: - case CSR_HPMCOUNTER4H: - case CSR_HPMCOUNTER5H: - case CSR_HPMCOUNTER6H: - case CSR_HPMCOUNTER7H: - case CSR_HPMCOUNTER8H: - case CSR_HPMCOUNTER9H: - case CSR_HPMCOUNTER10H: - case CSR_HPMCOUNTER11H: - case CSR_HPMCOUNTER12H: - case CSR_HPMCOUNTER13H: - case CSR_HPMCOUNTER14H: - case CSR_HPMCOUNTER15H: - case CSR_HPMCOUNTER16H: - case CSR_HPMCOUNTER17H: - case CSR_HPMCOUNTER18H: - case CSR_HPMCOUNTER19H: - case CSR_HPMCOUNTER20H: - case CSR_HPMCOUNTER21H: - case CSR_HPMCOUNTER22H: - case CSR_HPMCOUNTER23H: - case CSR_HPMCOUNTER24H: - case CSR_HPMCOUNTER25H: - case CSR_HPMCOUNTER26H: - case CSR_HPMCOUNTER27H: - case CSR_HPMCOUNTER28H: - case CSR_HPMCOUNTER29H: - case CSR_HPMCOUNTER30H: - case CSR_HPMCOUNTER31H: - case CSR_MCYCLEH: - case CSR_MINSTRETH: - case CSR_MHPMCOUNTER3H: - case CSR_MHPMCOUNTER4H: - case CSR_MHPMCOUNTER5H: - case CSR_MHPMCOUNTER6H: - case CSR_MHPMCOUNTER7H: - case CSR_MHPMCOUNTER8H: - case CSR_MHPMCOUNTER9H: - case CSR_MHPMCOUNTER10H: - case CSR_MHPMCOUNTER11H: - case CSR_MHPMCOUNTER12H: - case CSR_MHPMCOUNTER13H: - case CSR_MHPMCOUNTER14H: - case CSR_MHPMCOUNTER15H: - case CSR_MHPMCOUNTER16H: - case CSR_MHPMCOUNTER17H: - case CSR_MHPMCOUNTER18H: - case CSR_MHPMCOUNTER19H: - case CSR_MHPMCOUNTER20H: - case CSR_MHPMCOUNTER21H: - case CSR_MHPMCOUNTER22H: - case CSR_MHPMCOUNTER23H: - case CSR_MHPMCOUNTER24H: - case CSR_MHPMCOUNTER25H: - case CSR_MHPMCOUNTER26H: - case CSR_MHPMCOUNTER27H: - case CSR_MHPMCOUNTER28H: - case CSR_MHPMCOUNTER29H: - case CSR_MHPMCOUNTER30H: - case CSR_MHPMCOUNTER31H: - r->exist = riscv_xlen(target) == 32; - break; - - case CSR_VSTART: - case CSR_VXSAT: - case CSR_VXRM: - case CSR_VL: - case CSR_VTYPE: - case CSR_VLENB: - r->exist = riscv_supports_extension(target, 'V'); - break; - } - - if (!r->exist && !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) { - sprintf(reg_name, "priv"); - r->group = "general"; - r->feature = &feature_virtual; - r->size = 8; - - } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) { - r->caller_save = false; - r->exist = riscv_supports_extension(target, 'V') && info->vlenb; - r->size = info->vlenb * 8; - sprintf(reg_name, "v%d", number - GDB_REGNO_V0); - r->group = "vector"; - r->feature = &feature_vector; - r->reg_data_type = &info->type_vector; - - } else if (number >= GDB_REGNO_COUNT) { - /* Custom registers. */ - assert(!list_empty(&info->expose_custom)); - - range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list); - - unsigned int custom_number = range->low + custom_within_range; - - r->group = "custom"; - r->feature = &feature_custom; - r->arch_info = calloc(1, sizeof(riscv_reg_info_t)); - if (!r->arch_info) - return ERROR_FAIL; - ((riscv_reg_info_t *) r->arch_info)->target = target; - ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number; - sprintf(reg_name, "custom%d", custom_number); - - if (range->name) { - *reg_name = 0; - r->name = range->name; - } - - LOG_DEBUG("Exposing additional custom register %d (name=%s)", - number, range->name ? range->name : reg_name); - - custom_within_range++; - if (custom_within_range > range->high - range->low) { - custom_within_range = 0; - list_rotate_left(&info->expose_custom); - } - } - - if (reg_name[0]) { - 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 = calloc(1, DIV_ROUND_UP(r->size, 8)); - } + r->triggers_enumerated = true; + r->trigger_count = t; + LOG_TARGET_INFO(target, "Found %d triggers", r->trigger_count); + free(r->reserved_triggers); + r->reserved_triggers = calloc(t, sizeof(*r->reserved_triggers)); + create_wp_trigger_cache(target); return ERROR_OK; } - -void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field, +void riscv_add_bscan_tunneled_scan(struct jtag_tap *tap, const struct scan_field *field, riscv_bscan_tunneled_scan_context_t *ctxt) { - jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE); + jtag_add_ir_scan(tap, &select_user4, TAP_IDLE); memset(ctxt->tunneled_dr, 0, sizeof(ctxt->tunneled_dr)); if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) { @@ -4449,5 +6332,5 @@ void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *fie ctxt->tunneled_dr[3].num_bits = 3; ctxt->tunneled_dr[3].out_value = bscan_zero; } - jtag_add_dr_scan(target->tap, ARRAY_SIZE(ctxt->tunneled_dr), ctxt->tunneled_dr, TAP_IDLE); + jtag_add_dr_scan(tap, ARRAY_SIZE(ctxt->tunneled_dr), ctxt->tunneled_dr, TAP_IDLE); } diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index f06b5f516..2a0a9b95f 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef RISCV_H -#define RISCV_H +#ifndef OPENOCD_TARGET_RISCV_RISCV_H +#define OPENOCD_TARGET_RISCV_RISCV_H struct riscv_program; @@ -9,28 +9,33 @@ struct riscv_program; #include "opcodes.h" #include "gdb_regs.h" #include "jtag/jtag.h" -#include "target/register.h" #include "target/semihosting_common.h" +#include "target/target.h" +#include "target/register.h" #include +#include #define RISCV_COMMON_MAGIC 0x52495356U -/* The register cache is statically allocated. */ -#define RISCV_MAX_HARTS 1024 -#define RISCV_MAX_REGISTERS 5000 +#define RISCV_MAX_HARTS ((int)BIT(20)) #define RISCV_MAX_TRIGGERS 32 #define RISCV_MAX_HWBPS 16 +#define RISCV_MAX_DMS 100 -#define DEFAULT_COMMAND_TIMEOUT_SEC 2 -#define DEFAULT_RESET_TIMEOUT_SEC 30 +#define DEFAULT_COMMAND_TIMEOUT_SEC 5 #define RISCV_SATP_MODE(xlen) ((xlen) == 32 ? SATP32_MODE : SATP64_MODE) #define RISCV_SATP_PPN(xlen) ((xlen) == 32 ? SATP32_PPN : SATP64_PPN) +#define RISCV_HGATP_MODE(xlen) ((xlen) == 32 ? HGATP32_MODE : HGATP64_MODE) +#define RISCV_HGATP_PPN(xlen) ((xlen) == 32 ? HGATP32_PPN : HGATP64_PPN) #define RISCV_PGSHIFT 12 +#define RISCV_PGSIZE BIT(RISCV_PGSHIFT) +#define RISCV_PGBASE(addr) ((addr) & ~(RISCV_PGSIZE - 1)) +#define RISCV_PGOFFSET(addr) ((addr) & (RISCV_PGSIZE - 1)) -# define PG_MAX_LEVEL 4 +#define PG_MAX_LEVEL 5 -#define RISCV_NUM_MEM_ACCESS_METHODS 3 +#define RISCV_BATCH_ALLOC_SIZE 128 extern struct target_type riscv011_target; extern struct target_type riscv013_target; @@ -42,16 +47,30 @@ typedef uint64_t riscv_reg_t; typedef uint32_t riscv_insn_t; typedef uint64_t riscv_addr_t; +enum yes_no_maybe { + YNM_MAYBE, + YNM_YES, + YNM_NO +}; + enum riscv_mem_access_method { - RISCV_MEM_ACCESS_UNSPECIFIED, RISCV_MEM_ACCESS_PROGBUF, RISCV_MEM_ACCESS_SYSBUS, - RISCV_MEM_ACCESS_ABSTRACT + RISCV_MEM_ACCESS_ABSTRACT, + RISCV_MEM_ACCESS_MAX_METHODS_NUM }; +enum riscv_virt2phys_mode { + RISCV_VIRT2PHYS_MODE_HW, + RISCV_VIRT2PHYS_MODE_SW, + RISCV_VIRT2PHYS_MODE_OFF +}; + +const char *riscv_virt2phys_mode_to_str(enum riscv_virt2phys_mode mode); + enum riscv_halt_reason { RISCV_HALT_INTERRUPT, - RISCV_HALT_BREAKPOINT, + RISCV_HALT_EBREAK, RISCV_HALT_SINGLESTEP, RISCV_HALT_TRIGGER, RISCV_HALT_UNKNOWN, @@ -59,8 +78,24 @@ enum riscv_halt_reason { RISCV_HALT_ERROR }; +enum riscv_isrmasking_mode { + /* RISCV_ISRMASK_AUTO, */ /* not supported yet */ + RISCV_ISRMASK_OFF, + /* RISCV_ISRMASK_ON, */ /* not supported yet */ + RISCV_ISRMASK_STEPONLY, +}; + +enum riscv_hart_state { + RISCV_STATE_NON_EXISTENT, + RISCV_STATE_RUNNING, + RISCV_STATE_HALTED, + RISCV_STATE_UNAVAILABLE +}; + +/* RISC-V-specific data assigned to a register. */ typedef struct { struct target *target; + /* Abstract command's regno for a custom register. */ unsigned int custom_number; } riscv_reg_info_t; @@ -87,6 +122,49 @@ typedef struct { char *name; } range_list_t; +#define DTM_DTMCS_VERSION_UNKNOWN ((unsigned int)-1) +#define RISCV_TINFO_VERSION_UNKNOWN (-1) + +#define RISCV013_DTMCS_ABITS_MIN 7 +#define RISCV013_DTMCS_ABITS_MAX 32 + +struct reg_name_table { + unsigned int num_entries; + char **reg_names; +}; + +struct riscv_mem_access_args { + target_addr_t address; + + const uint8_t *write_buffer; + uint8_t *read_buffer; + + uint32_t size; + uint32_t count; + uint32_t increment; +}; + +static inline bool +riscv_mem_access_is_valid(const struct riscv_mem_access_args args) +{ + return !args.read_buffer != !args.write_buffer; +} + +static inline bool +riscv_mem_access_is_read(const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_valid(args)); + return !args.write_buffer && args.read_buffer; +} + +static inline bool +riscv_mem_access_is_write(const struct riscv_mem_access_args args) +{ + assert(riscv_mem_access_is_valid(args)); + return !args.read_buffer && args.write_buffer; +} + + struct riscv_info { unsigned int common_magic; @@ -95,36 +173,57 @@ struct riscv_info { struct command_context *cmd_ctx; void *version_specific; - /* The hart that is currently being debugged. Note that this is - * different than the hartid that the RTOS is expected to use. This - * one will change all the time, it's more of a global argument to - * every function than an actual */ - int current_hartid; - - /* Single buffer that contains all register names, instead of calling - * malloc for each register. Needs to be freed when reg_list is freed. */ - char *reg_names; + struct reg_name_table custom_register_names; + char **reg_names; /* It's possible that each core has a different supported ISA set. */ int xlen; + /* TODO: use the value from the register cache instead. */ riscv_reg_t misa; - /* Cached value of vlenb. 0 if vlenb is not readable for some reason. */ + /* TODO: use the value from the register cache instead. + * Cached value of vlenb. 0 indicates there is no vector support. + * Note that you can have vector support without misa.V set, because + * Zve* extensions implement vector registers without setting misa.V. */ unsigned int vlenb; /* The number of triggers per hart. */ unsigned int trigger_count; - /* For each physical trigger, contains -1 if the hwbp is available, or the - * unique_id of the breakpoint/watchpoint that is using it. + /* Data structure to record known unsupported tdata1+tdata2 trigger CSR values. + * This is to avoid repetitive attempts to set trigger configurations that are already + * known to be unsupported in the HW. + * A separate data structure is created for each trigger. */ + struct list_head *wp_triggers_negative_cache; + + /* record the tinfo of each trigger */ + unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS]; + + /* Version of the implemented Sdtrig extension */ + int tinfo_version; + + /* Record if single-step is needed prior to resuming + * from a software breakpoint or trigger. + * Single-step is needed if the instruction that + * caused the halt was not retired. That is, + * when we halted "before" that instruction. + */ + bool need_single_step; + + /* For each physical trigger contains: + * -1: the hwbp is available + * -4: The trigger is used by the itrigger command + * -5: The trigger is used by the etrigger command + * >= 0: unique_id of the breakpoint/watchpoint that is using it. * Note that in RTOS mode the triggers are the same across all harts the * target controls, while otherwise only a single hart is controlled. */ - int trigger_unique_id[RISCV_MAX_HWBPS]; + int64_t trigger_unique_id[RISCV_MAX_HWBPS]; - /* The number of entries in the debug buffer. */ - int debug_buffer_size; + /* The unique id of the trigger that caused the most recent halt. If the + * most recent halt was not caused by a trigger, then this is -1. */ + int64_t trigger_hit; - /* This hart contains an implicit ebreak at the end of the program buffer. */ - bool impebreak; + /* The configured approach to translate virtual addresses to physical */ + enum riscv_virt2phys_mode virt2phys_mode; bool triggers_enumerated; @@ -137,21 +236,37 @@ struct riscv_info { /* This target was selected using hasel. */ bool selected; + /* Used by riscv_openocd_poll(). */ + bool halted_needs_event_callback; + enum target_event halted_callback_event; + unsigned int halt_group_repoll_count; + + enum riscv_isrmasking_mode isrmask_mode; + /* Helper functions that target the various RISC-V debug spec * implementations. */ - int (*get_register)(struct target *target, riscv_reg_t *value, int regid); - int (*set_register)(struct target *target, int regid, uint64_t value); - int (*get_register_buf)(struct target *target, uint8_t *buf, int regno); - int (*set_register_buf)(struct target *target, int regno, - const uint8_t *buf); - int (*select_current_hart)(struct target *target); - bool (*is_halted)(struct target *target); + int (*select_target)(struct target *target); + int (*get_hart_state)(struct target *target, enum riscv_hart_state *state); /* Resume this target, as well as every other prepped target that can be * resumed near-simultaneously. Clear the prepped flag on any target that * was resumed. */ int (*resume_go)(struct target *target); int (*step_current_hart)(struct target *target); - int (*on_halt)(struct target *target); + + /* These get called from riscv_poll_hart(), which is a house of cards + * together with openocd_poll(), so be careful not to upset things too + * much. */ + int (*handle_became_halted)(struct target *target, + enum riscv_hart_state previous_riscv_state); + int (*handle_became_running)(struct target *target, + enum riscv_hart_state previous_riscv_state); + int (*handle_became_unavailable)(struct target *target, + enum riscv_hart_state previous_riscv_state); + + /* Called periodically (no guarantees about frequency), while there's + * nothing else going on. */ + int (*tick)(struct target *target); + /* Get this target as ready as possible to resume, without actually * resuming. */ int (*resume_prep)(struct target *target); @@ -159,14 +274,14 @@ struct riscv_info { int (*halt_go)(struct target *target); int (*on_step)(struct target *target); enum riscv_halt_reason (*halt_reason)(struct target *target); - int (*write_debug_buffer)(struct target *target, unsigned int index, - riscv_insn_t d); - riscv_insn_t (*read_debug_buffer)(struct target *target, unsigned int index); - int (*execute_debug_buffer)(struct target *target); - int (*dmi_write_u64_bits)(struct target *target); - void (*fill_dmi_write_u64)(struct target *target, char *buf, int a, uint64_t d); - void (*fill_dmi_read_u64)(struct target *target, char *buf, int a); - void (*fill_dmi_nop_u64)(struct target *target, char *buf); + int (*write_progbuf)(struct target *target, unsigned int index, riscv_insn_t d); + riscv_insn_t (*read_progbuf)(struct target *target, unsigned int index); + int (*execute_progbuf)(struct target *target, uint32_t *cmderr); + int (*invalidate_cached_progbuf)(struct target *target); + unsigned int (*get_dmi_address_bits)(const struct target *target); + void (*fill_dmi_write)(const struct target *target, uint8_t *buf, uint32_t a, uint32_t d); + void (*fill_dmi_read)(const struct target *target, uint8_t *buf, uint32_t a); + void (*fill_dm_nop)(const struct target *target, uint8_t *buf); int (*authdata_read)(struct target *target, uint32_t *value, unsigned int index); int (*authdata_write)(struct target *target, uint32_t value, unsigned int index); @@ -174,20 +289,29 @@ struct riscv_info { int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address); int (*dmi_write)(struct target *target, uint32_t address, uint32_t value); + bool (*get_impebreak)(const struct target *target); + unsigned int (*get_progbufsize)(const struct target *target); + + /* Get the DMI address of target's DM's register. + * The function should return the passed address + * if the target is not assigned a DM yet. + */ + uint32_t (*get_dmi_address)(const struct target *target, uint32_t dm_address); + int (*sample_memory)(struct target *target, struct riscv_sample_buf *buf, riscv_sample_config_t *config, int64_t until_ms); - int (*read_memory)(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment); + int (*access_memory)(struct target *target, const struct riscv_mem_access_args args); - /* How many harts are attached to the DM that this target is attached to? */ - int (*hart_count)(struct target *target); unsigned int (*data_bits)(struct target *target); COMMAND_HELPER((*print_info), struct target *target); + /* Storage for arch_info of non-custom registers. */ + riscv_reg_info_t shared_reg_info; + /* Storage for vector register types. */ struct reg_data_type_vector vector_uint8; struct reg_data_type_vector vector_uint16; @@ -203,18 +327,16 @@ struct riscv_info { struct reg_data_type_union vector_union; struct reg_data_type type_vector; - /* Set when trigger registers are changed by the user. This indicates we eed - * to beware that we may hit a trigger that we didn't realize had been set. */ - bool manual_hwbp_set; + bool *reserved_triggers; /* Memory access methods to use, ordered by priority, highest to lowest. */ - int mem_access_methods[RISCV_NUM_MEM_ACCESS_METHODS]; + enum riscv_mem_access_method mem_access_methods[RISCV_MEM_ACCESS_MAX_METHODS_NUM]; + + unsigned int num_enabled_mem_access_methods; /* Different memory regions may need different methods but single configuration is applied * for all. Following flags are used to warn only once about failing memory access method. */ - bool mem_access_progbuf_warn; - bool mem_access_sysbus_warn; - bool mem_access_abstract_warn; + bool mem_access_warn[RISCV_MEM_ACCESS_MAX_METHODS_NUM]; /* In addition to the ones in the standard spec, we'll also expose additional * CSRs in this list. */ @@ -224,10 +346,47 @@ struct riscv_info { * from range 0xc000 ... 0xffff. */ struct list_head expose_custom; + /* The list of registers to mark as "hidden". Hidden registers are available + * but do not appear in gdb targets description or reg command output. */ + struct list_head hide_csr; + riscv_sample_config_t sample_config; struct riscv_sample_buf sample_buf; + + /* Track when we were last asked to do something substantial. */ + int64_t last_activity; + + enum yes_no_maybe vsew64_supported; + + bool range_trigger_fallback_encountered; + + bool wp_allow_equality_match_trigger; + bool wp_allow_napot_trigger; + bool wp_allow_ge_lt_trigger; + + bool autofence; }; +enum riscv_priv_mode { + RISCV_MODE_M, + RISCV_MODE_S, + RISCV_MODE_U, + RISCV_MODE_VS, + RISCV_MODE_VU, + N_RISCV_MODE +}; + +struct riscv_private_config { + bool dcsr_ebreak_fields[N_RISCV_MODE]; +}; + +static inline struct riscv_private_config +*riscv_private_config(const struct target *target) +{ + assert(target->private_config); + return target->private_config; +} + COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key, unsigned int value); @@ -240,6 +399,7 @@ typedef struct { const char *name; int level; unsigned int va_bits; + /* log2(PTESIZE) */ unsigned int pte_shift; unsigned int vpn_shift[PG_MAX_LEVEL]; unsigned int vpn_mask[PG_MAX_LEVEL]; @@ -249,16 +409,11 @@ typedef struct { unsigned int pa_ppn_mask[PG_MAX_LEVEL]; } virt2phys_info_t; +bool riscv_virt2phys_mode_is_hw(const struct target *target); +bool riscv_virt2phys_mode_is_sw(const struct target *target); + /* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/ -extern int riscv_command_timeout_sec; - -/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ -extern int riscv_reset_timeout_sec; - -extern bool riscv_enable_virtual; -extern bool riscv_ebreakm; -extern bool riscv_ebreaks; -extern bool riscv_ebreaku; +int riscv_get_command_timeout_sec(void); /* Everything needs the RISC-V specific info structure, so here's a nice macro * that provides that. */ @@ -279,13 +434,14 @@ extern struct scan_field select_dtmcontrol; extern struct scan_field select_dbus; extern struct scan_field select_idcode; +int dtmcs_scan(struct jtag_tap *tap, uint32_t out, uint32_t *in_ptr); + extern struct scan_field *bscan_tunneled_select_dmi; extern uint32_t bscan_tunneled_select_dmi_num_fields; typedef enum { BSCAN_TUNNEL_NESTED_TAP, BSCAN_TUNNEL_DATA_REGISTER } bscan_tunnel_type_t; -extern int bscan_tunnel_ir_width; +extern uint8_t bscan_tunnel_ir_width; -uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out); -void select_dmi_via_bscan(struct target *target); +void select_dmi_via_bscan(struct jtag_tap *tap); /*** OpenOCD Interface */ int riscv_openocd_poll(struct target *target); @@ -299,52 +455,37 @@ int riscv_openocd_step( bool handle_breakpoints ); -int riscv_openocd_assert_reset(struct target *target); -int riscv_openocd_deassert_reset(struct target *target); - /*** RISC-V Interface ***/ -bool riscv_supports_extension(struct target *target, char letter); +bool riscv_supports_extension(const struct target *target, char letter); /* Returns XLEN for the given (or current) hart. */ unsigned int riscv_xlen(const struct target *target); -int riscv_xlen_of_hart(const struct target *target); -/* Sets the current hart, which is the hart that will actually be used when - * issuing debug commands. */ -int riscv_set_current_hartid(struct target *target, int hartid); -int riscv_select_current_hart(struct target *target); -int riscv_current_hartid(const struct target *target); +/* Returns VLENB for the given (or current) hart. */ +unsigned int riscv_vlenb(const struct target *target); /*** Support functions for the RISC-V 'RTOS', which provides multihart support * without requiring multiple targets. */ -/* Lists the number of harts in the system, which are assumed to be - * consecutive and start with mhartid=0. */ -int riscv_count_harts(struct target *target); - -/** Set register, updating the cache. */ -int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v); -/** Get register, from the cache if it's in there. */ -int riscv_get_register(struct target *target, riscv_reg_t *value, - enum gdb_regno r); - /* Checks the state of the current hart -- "is_halted" checks the actual * on-device register. */ -bool riscv_is_halted(struct target *target); +int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state); /* These helper functions let the generic program interface get target-specific * information. */ -size_t riscv_debug_buffer_size(struct target *target); +unsigned int riscv_progbuf_size(struct target *target); -riscv_insn_t riscv_read_debug_buffer(struct target *target, int index); -int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn); -int riscv_execute_debug_buffer(struct target *target); +riscv_insn_t riscv_read_progbuf(struct target *target, int index); +int riscv_write_progbuf(struct target *target, unsigned int index, riscv_insn_t insn); +int riscv_execute_progbuf(struct target *target, uint32_t *cmderr); -void riscv_fill_dmi_nop_u64(struct target *target, char *buf); -void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d); -void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a); -int riscv_dmi_write_u64_bits(struct target *target); +void riscv_fill_dm_nop(const struct target *target, uint8_t *buf); +void riscv_fill_dmi_write(const struct target *target, uint8_t *buf, uint32_t a, uint32_t d); +void riscv_fill_dmi_read(const struct target *target, uint8_t *buf, uint32_t a); +unsigned int riscv_get_dmi_address_bits(const struct target *target); + +uint32_t riscv_get_dmi_address(const struct target *target, uint32_t dm_address); int riscv_enumerate_triggers(struct target *target); @@ -352,16 +493,14 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint); int riscv_remove_watchpoint(struct target *target, struct watchpoint *watchpoint); -int riscv_init_registers(struct target *target); - void riscv_semihosting_init(struct target *target); enum semihosting_result riscv_semihosting(struct target *target, int *retval); -void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field, +void riscv_add_bscan_tunneled_scan(struct jtag_tap *tap, const struct scan_field *field, riscv_bscan_tunneled_scan_context_t *ctxt); int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer); int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer); -#endif +#endif /* OPENOCD_TARGET_RISCV_RISCV_H */ diff --git a/src/target/riscv/riscv_reg.c b/src/target/riscv/riscv_reg.c new file mode 100644 index 000000000..e35cd7f4f --- /dev/null +++ b/src/target/riscv/riscv_reg.c @@ -0,0 +1,990 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gdb_regs.h" +#include "riscv.h" +#include "riscv_reg.h" +#include "riscv_reg_impl.h" +/** + * TODO: Currently `reg->get/set` is implemented in terms of + * `riscv_get/set_register`. However, the intention behind + * `riscv_get/set_register` is to work with the cache, therefore it accesses + * and modifyes register cache directly. The idea is to implement + * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and + * `reg->get/set`. + * Once this is done, the following includes should be removed. + */ +#include "debug_defines.h" +#include "riscv-011.h" +#include "riscv-013.h" +#include "field_helpers.h" + +static const char * const default_reg_names[GDB_REGNO_COUNT] = { + [GDB_REGNO_ZERO] = "zero", + [GDB_REGNO_RA] = "ra", + [GDB_REGNO_SP] = "sp", + [GDB_REGNO_GP] = "gp", + [GDB_REGNO_TP] = "tp", + [GDB_REGNO_T0] = "t0", + [GDB_REGNO_T1] = "t1", + [GDB_REGNO_T2] = "t2", + [GDB_REGNO_FP] = "fp", + [GDB_REGNO_S1] = "s1", + [GDB_REGNO_A0] = "a0", + [GDB_REGNO_A1] = "a1", + [GDB_REGNO_A2] = "a2", + [GDB_REGNO_A3] = "a3", + [GDB_REGNO_A4] = "a4", + [GDB_REGNO_A5] = "a5", + [GDB_REGNO_A6] = "a6", + [GDB_REGNO_A7] = "a7", + [GDB_REGNO_S2] = "s2", + [GDB_REGNO_S3] = "s3", + [GDB_REGNO_S4] = "s4", + [GDB_REGNO_S5] = "s5", + [GDB_REGNO_S6] = "s6", + [GDB_REGNO_S7] = "s7", + [GDB_REGNO_S8] = "s8", + [GDB_REGNO_S9] = "s9", + [GDB_REGNO_S10] = "s10", + [GDB_REGNO_S11] = "s11", + [GDB_REGNO_T3] = "t3", + [GDB_REGNO_T4] = "t4", + [GDB_REGNO_T5] = "t5", + [GDB_REGNO_T6] = "t6", + [GDB_REGNO_PC] = "pc", + [GDB_REGNO_PRIV] = "priv", + [GDB_REGNO_FT0] = "ft0", + [GDB_REGNO_FT1] = "ft1", + [GDB_REGNO_FT2] = "ft2", + [GDB_REGNO_FT3] = "ft3", + [GDB_REGNO_FT4] = "ft4", + [GDB_REGNO_FT5] = "ft5", + [GDB_REGNO_FT6] = "ft6", + [GDB_REGNO_FT7] = "ft7", + [GDB_REGNO_FS0] = "fs0", + [GDB_REGNO_FS1] = "fs1", + [GDB_REGNO_FA0] = "fa0", + [GDB_REGNO_FA1] = "fa1", + [GDB_REGNO_FA2] = "fa2", + [GDB_REGNO_FA3] = "fa3", + [GDB_REGNO_FA4] = "fa4", + [GDB_REGNO_FA5] = "fa5", + [GDB_REGNO_FA6] = "fa6", + [GDB_REGNO_FA7] = "fa7", + [GDB_REGNO_FS2] = "fs2", + [GDB_REGNO_FS3] = "fs3", + [GDB_REGNO_FS4] = "fs4", + [GDB_REGNO_FS5] = "fs5", + [GDB_REGNO_FS6] = "fs6", + [GDB_REGNO_FS7] = "fs7", + [GDB_REGNO_FS8] = "fs8", + [GDB_REGNO_FS9] = "fs9", + [GDB_REGNO_FS10] = "fs10", + [GDB_REGNO_FS11] = "fs11", + [GDB_REGNO_FT8] = "ft8", + [GDB_REGNO_FT9] = "ft9", + [GDB_REGNO_FT10] = "ft10", + [GDB_REGNO_FT11] = "ft11", + + #define DECLARE_CSR(csr_name, number)[(number) + GDB_REGNO_CSR0] = #csr_name, + #include "encoding.h" + #undef DECLARE_CSR +}; + +static void free_custom_register_names(struct target *target) +{ + RISCV_INFO(info); + + if (!info->custom_register_names.reg_names) + return; + + for (unsigned int i = 0; i < info->custom_register_names.num_entries; i++) + free(info->custom_register_names.reg_names[i]); + free(info->custom_register_names.reg_names); + info->custom_register_names.reg_names = NULL; +} + +static void free_reg_names(struct target *target) +{ + RISCV_INFO(info); + + if (!info->reg_names) + return; + + for (unsigned int i = 0; i < GDB_REGNO_COUNT; ++i) + free(info->reg_names[i]); + free(info->reg_names); + info->reg_names = NULL; + + free_custom_register_names(target); +} + +static char *init_reg_name(const char *name) +{ + const int size_buf = strlen(name) + 1; + + char * const buf = calloc(size_buf, sizeof(char)); + if (!buf) { + LOG_ERROR("Failed to allocate memory for a register name."); + return NULL; + } + strcpy(buf, name); + return buf; +} + +static void init_custom_csr_names(const struct target *target) +{ + RISCV_INFO(info); + range_list_t *entry; + + list_for_each_entry(entry, &info->expose_csr, list) { + if (!entry->name) + continue; + assert(entry->low == entry->high); + const unsigned int regno = entry->low + GDB_REGNO_CSR0; + assert(regno <= GDB_REGNO_CSR4095); + if (info->reg_names[regno]) + return; + info->reg_names[regno] = init_reg_name(entry->name); + } +} + +static char *init_reg_name_with_prefix(const char *name_prefix, + unsigned int num) +{ + const int size_buf = snprintf(NULL, 0, "%s%d", name_prefix, num) + 1; + + char * const buf = calloc(size_buf, sizeof(char)); + if (!buf) { + LOG_ERROR("Failed to allocate memory for a register name."); + return NULL; + } + int result = snprintf(buf, size_buf, "%s%d", name_prefix, num); + assert(result > 0 && result <= (size_buf - 1)); + return buf; +} + +const char *riscv_reg_gdb_regno_name(const struct target *target, enum gdb_regno regno) +{ + RISCV_INFO(info); + + if (regno >= GDB_REGNO_COUNT) { + assert(info->custom_register_names.reg_names); + assert(regno - GDB_REGNO_COUNT <= info->custom_register_names.num_entries); + return info->custom_register_names.reg_names[regno - GDB_REGNO_COUNT]; + } + + if (!info->reg_names) + info->reg_names = calloc(GDB_REGNO_COUNT, sizeof(char *)); + + if (info->reg_names[regno]) + return info->reg_names[regno]; + if (default_reg_names[regno]) + return default_reg_names[regno]; + if (regno <= GDB_REGNO_XPR31) { + info->reg_names[regno] = init_reg_name_with_prefix("x", regno - GDB_REGNO_ZERO); + return info->reg_names[regno]; + } + if (regno <= GDB_REGNO_V31 && regno >= GDB_REGNO_V0) { + info->reg_names[regno] = init_reg_name_with_prefix("v", regno - GDB_REGNO_V0); + return info->reg_names[regno]; + } + if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) { + init_custom_csr_names(target); + if (!info->reg_names[regno]) + info->reg_names[regno] = init_reg_name_with_prefix("csr", regno - GDB_REGNO_CSR0); + return info->reg_names[regno]; + } + assert(!"Encountered uninitialized entry in reg_names table"); + + return NULL; +} + +struct target *riscv_reg_impl_get_target(const struct reg *reg) +{ + assert(riscv_reg_impl_is_initialized(reg)); + return ((const riscv_reg_info_t *)reg->arch_info)->target; +} + +static struct reg_feature *gdb_regno_feature(uint32_t regno) +{ + if (regno <= GDB_REGNO_XPR31 || regno == GDB_REGNO_PC) { + static struct reg_feature feature_cpu = { + .name = "org.gnu.gdb.riscv.cpu" + }; + return &feature_cpu; + } + if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) || + regno == GDB_REGNO_FFLAGS || + regno == GDB_REGNO_FRM || + regno == GDB_REGNO_FCSR) { + static struct reg_feature feature_fpu = { + .name = "org.gnu.gdb.riscv.fpu" + }; + return &feature_fpu; + } + if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) { + static struct reg_feature feature_vector = { + .name = "org.gnu.gdb.riscv.vector" + }; + return &feature_vector; + } + if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) { + static struct reg_feature feature_csr = { + .name = "org.gnu.gdb.riscv.csr" + }; + return &feature_csr; + } + if (regno == GDB_REGNO_PRIV) { + static struct reg_feature feature_virtual = { + .name = "org.gnu.gdb.riscv.virtual" + }; + return &feature_virtual; + } + assert(regno >= GDB_REGNO_COUNT); + static struct reg_feature feature_custom = { + .name = "org.gnu.gdb.riscv.custom" + }; + return &feature_custom; +} + +static bool gdb_regno_caller_save(uint32_t regno) +{ + return regno <= GDB_REGNO_XPR31 || + regno == GDB_REGNO_PC || + (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31); +} + +static struct reg_data_type *gdb_regno_reg_data_type(const struct target *target, + uint32_t regno) +{ + if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) { + static struct reg_data_type type_ieee_single = { + .type = REG_TYPE_IEEE_SINGLE, + .id = "ieee_single" + }; + static struct reg_data_type type_ieee_double = { + .type = REG_TYPE_IEEE_DOUBLE, + .id = "ieee_double" + }; + static struct reg_data_type_union_field single_double_fields[] = { + {"float", &type_ieee_single, single_double_fields + 1}, + {"double", &type_ieee_double, NULL}, + }; + static struct reg_data_type_union single_double_union = { + .fields = single_double_fields + }; + static struct reg_data_type type_ieee_single_double = { + .type = REG_TYPE_ARCH_DEFINED, + .id = "FPU_FD", + .type_class = REG_TYPE_CLASS_UNION, + {.reg_type_union = &single_double_union} + }; + return riscv_supports_extension(target, 'D') ? + &type_ieee_single_double : + &type_ieee_single; + } + if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) { + RISCV_INFO(info); + return &info->type_vector; + } + return NULL; +} + +static const char *gdb_regno_group(uint32_t regno) +{ + if (regno <= GDB_REGNO_XPR31 || + regno == GDB_REGNO_PC || + regno == GDB_REGNO_PRIV) + return "general"; + if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) || + regno == GDB_REGNO_FFLAGS || + regno == GDB_REGNO_FRM || + regno == GDB_REGNO_FCSR) + return "float"; + if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) + return "csr"; + if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) + return "vector"; + assert(regno >= GDB_REGNO_COUNT); + return "custom"; +} + +uint32_t gdb_regno_size(const struct target *target, uint32_t regno) +{ + if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) + return riscv_supports_extension(target, 'D') ? 64 : 32; + if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) + return riscv_vlenb(target) * 8; + if (regno == GDB_REGNO_PRIV) + return 8; + if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) { + const unsigned int csr_number = regno - GDB_REGNO_CSR0; + switch (csr_number) { + case CSR_DCSR: + case CSR_MVENDORID: + case CSR_MCOUNTINHIBIT: + + case CSR_FFLAGS: + case CSR_FRM: + case CSR_FCSR: + + case CSR_SCOUNTEREN: + case CSR_MCOUNTEREN: + return 32; + } + } + return riscv_xlen(target); +} + +static bool vlenb_exists(const struct target *target) +{ + return riscv_vlenb(target) != 0; +} + +static bool reg_exists(const struct target *target, uint32_t regno) +{ + const struct reg * const reg = riscv_reg_impl_cache_entry(target, regno); + assert(riscv_reg_impl_is_initialized(reg)); + return reg->exist; +} + +static bool is_known_standard_csr(unsigned int csr_num) +{ + static const bool is_csr_in_buf[GDB_REGNO_CSR4095 - GDB_REGNO_CSR0 + 1] = { + #define DECLARE_CSR(csr_name, number)[number] = true, + #include "encoding.h" + #undef DECLARE_CSR + }; + assert(csr_num < ARRAY_SIZE(is_csr_in_buf)); + + return is_csr_in_buf[csr_num]; +} + +bool riscv_reg_impl_gdb_regno_exist(const struct target *target, uint32_t regno) +{ + switch (regno) { + case GDB_REGNO_VLENB: + case GDB_REGNO_MTOPI: + case GDB_REGNO_MTOPEI: + assert(false + && "Existence of other registers is determined " + "depending on existence of these ones, so " + "whether these register exist or not should be " + "set explicitly."); + }; + + if (regno <= GDB_REGNO_XPR15 || + regno == GDB_REGNO_PC || + regno == GDB_REGNO_PRIV) + return true; + if (regno > GDB_REGNO_XPR15 && regno <= GDB_REGNO_XPR31) + return !riscv_supports_extension(target, 'E'); + if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) + return riscv_supports_extension(target, 'F'); + if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) + return vlenb_exists(target); + if (regno >= GDB_REGNO_COUNT) + return true; + assert(regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095); + const unsigned int csr_number = regno - GDB_REGNO_CSR0; + switch (csr_number) { + case CSR_FFLAGS: + case CSR_FRM: + case CSR_FCSR: + return riscv_supports_extension(target, 'F'); + case CSR_VSTART: + case CSR_VXSAT: + case CSR_VXRM: + case CSR_VL: + case CSR_VCSR: + case CSR_VTYPE: + return vlenb_exists(target); + case CSR_SCOUNTEREN: + case CSR_SSTATUS: + case CSR_STVEC: + case CSR_SIP: + case CSR_SIE: + case CSR_SSCRATCH: + case CSR_SEPC: + case CSR_SCAUSE: + case CSR_STVAL: + case CSR_SATP: + return riscv_supports_extension(target, 'S'); + 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." */ + return riscv_supports_extension(target, 'S') || + riscv_supports_extension(target, 'N'); + + case CSR_PMPCFG1: + case CSR_PMPCFG3: + case CSR_CYCLEH: + case CSR_TIMEH: + case CSR_INSTRETH: + case CSR_HPMCOUNTER3H: + case CSR_HPMCOUNTER4H: + case CSR_HPMCOUNTER5H: + case CSR_HPMCOUNTER6H: + case CSR_HPMCOUNTER7H: + case CSR_HPMCOUNTER8H: + case CSR_HPMCOUNTER9H: + case CSR_HPMCOUNTER10H: + case CSR_HPMCOUNTER11H: + case CSR_HPMCOUNTER12H: + case CSR_HPMCOUNTER13H: + case CSR_HPMCOUNTER14H: + case CSR_HPMCOUNTER15H: + case CSR_HPMCOUNTER16H: + case CSR_HPMCOUNTER17H: + case CSR_HPMCOUNTER18H: + case CSR_HPMCOUNTER19H: + case CSR_HPMCOUNTER20H: + case CSR_HPMCOUNTER21H: + case CSR_HPMCOUNTER22H: + case CSR_HPMCOUNTER23H: + case CSR_HPMCOUNTER24H: + case CSR_HPMCOUNTER25H: + case CSR_HPMCOUNTER26H: + case CSR_HPMCOUNTER27H: + case CSR_HPMCOUNTER28H: + case CSR_HPMCOUNTER29H: + case CSR_HPMCOUNTER30H: + case CSR_HPMCOUNTER31H: + case CSR_MCYCLEH: + case CSR_MINSTRETH: + case CSR_MHPMCOUNTER4H: + case CSR_MHPMCOUNTER5H: + case CSR_MHPMCOUNTER6H: + case CSR_MHPMCOUNTER7H: + case CSR_MHPMCOUNTER8H: + case CSR_MHPMCOUNTER9H: + case CSR_MHPMCOUNTER10H: + case CSR_MHPMCOUNTER11H: + case CSR_MHPMCOUNTER12H: + case CSR_MHPMCOUNTER13H: + case CSR_MHPMCOUNTER14H: + case CSR_MHPMCOUNTER15H: + case CSR_MHPMCOUNTER16H: + case CSR_MHPMCOUNTER17H: + case CSR_MHPMCOUNTER18H: + case CSR_MHPMCOUNTER19H: + case CSR_MHPMCOUNTER20H: + case CSR_MHPMCOUNTER21H: + case CSR_MHPMCOUNTER22H: + case CSR_MHPMCOUNTER23H: + case CSR_MHPMCOUNTER24H: + case CSR_MHPMCOUNTER25H: + case CSR_MHPMCOUNTER26H: + case CSR_MHPMCOUNTER27H: + case CSR_MHPMCOUNTER28H: + case CSR_MHPMCOUNTER29H: + case CSR_MHPMCOUNTER30H: + case CSR_MHPMCOUNTER31H: + return riscv_xlen(target) == 32; + case CSR_MCOUNTEREN: + return riscv_supports_extension(target, 'U'); + /* Interrupts M-Mode CSRs. */ + case CSR_MISELECT: + case CSR_MIREG: + case CSR_MVIEN: + case CSR_MVIP: + case CSR_MIEH: + case CSR_MIPH: + return reg_exists(target, GDB_REGNO_MTOPI); + case CSR_MIDELEGH: + case CSR_MVIENH: + case CSR_MVIPH: + return reg_exists(target, GDB_REGNO_MTOPI) && + riscv_xlen(target) == 32 && + riscv_supports_extension(target, 'S'); + /* Interrupts S-Mode CSRs. */ + case CSR_SISELECT: + case CSR_SIREG: + case CSR_STOPI: + return reg_exists(target, GDB_REGNO_MTOPI) && + riscv_supports_extension(target, 'S'); + case CSR_STOPEI: + return reg_exists(target, GDB_REGNO_MTOPEI) && + riscv_supports_extension(target, 'S'); + case CSR_SIEH: + case CSR_SIPH: + return reg_exists(target, GDB_REGNO_MTOPI) && + riscv_xlen(target) == 32 && + riscv_supports_extension(target, 'S'); + /* Interrupts Hypervisor and VS CSRs. */ + case CSR_HVIEN: + case CSR_HVICTL: + case CSR_HVIPRIO1: + case CSR_HVIPRIO2: + case CSR_VSISELECT: + case CSR_VSIREG: + case CSR_VSTOPI: + return reg_exists(target, GDB_REGNO_MTOPI) && + riscv_supports_extension(target, 'H'); + case CSR_VSTOPEI: + return reg_exists(target, GDB_REGNO_MTOPEI) && + riscv_supports_extension(target, 'H'); + case CSR_HIDELEGH: + case CSR_HVIENH: + case CSR_HVIPH: + case CSR_HVIPRIO1H: + case CSR_HVIPRIO2H: + case CSR_VSIEH: + case CSR_VSIPH: + return reg_exists(target, GDB_REGNO_MTOPI) && + riscv_xlen(target) == 32 && + riscv_supports_extension(target, 'H'); + } + return is_known_standard_csr(csr_number); +} + +static unsigned int gdb_regno_custom_number(const struct target *target, uint32_t regno) +{ + if (regno < GDB_REGNO_COUNT) + return 0; + + RISCV_INFO(info); + assert(!list_empty(&info->expose_custom)); + range_list_t *range; + unsigned int regno_start = GDB_REGNO_COUNT; + unsigned int start = 0; + unsigned int offset = 0; + list_for_each_entry(range, &info->expose_custom, list) { + start = range->low; + assert(regno >= regno_start); + offset = regno - regno_start; + const unsigned int regs_in_range = range->high - range->low + 1; + if (offset < regs_in_range) + break; + regno_start += regs_in_range; + } + return start + offset; +} + +struct reg *riscv_reg_impl_cache_entry(const struct target *target, + uint32_t number) +{ + assert(target->reg_cache); + assert(target->reg_cache->reg_list); + assert(number < target->reg_cache->num_regs); + return &target->reg_cache->reg_list[number]; +} + +static int resize_reg(const struct target *target, uint32_t regno, bool exist, + uint32_t size) +{ + struct reg *reg = riscv_reg_impl_cache_entry(target, regno); + assert(riscv_reg_impl_is_initialized(reg)); + free(reg->value); + reg->size = size; + reg->exist = exist; + if (reg->exist) { + reg->value = malloc(DIV_ROUND_UP(reg->size, 8)); + if (!reg->value) { + LOG_ERROR("Failed to allocate memory."); + return ERROR_FAIL; + } + } else { + reg->value = NULL; + } + assert(riscv_reg_impl_is_initialized(reg)); + return ERROR_OK; +} + +int riscv_reg_impl_set_exist(const struct target *target, uint32_t regno, bool exist) +{ + const struct reg *reg = riscv_reg_impl_cache_entry(target, regno); + assert(riscv_reg_impl_is_initialized(reg)); + return resize_reg(target, regno, exist, reg->size); +} + +int riscv_reg_impl_init_cache_entry(struct target *target, uint32_t regno, + bool exist, const struct reg_arch_type *reg_type) +{ + struct reg * const reg = riscv_reg_impl_cache_entry(target, regno); + if (riscv_reg_impl_is_initialized(reg)) + return ERROR_OK; + reg->number = regno; + reg->type = reg_type; + reg->dirty = false; + reg->valid = false; + reg->hidden = false; + reg->name = riscv_reg_gdb_regno_name(target, regno); + reg->feature = gdb_regno_feature(regno); + reg->caller_save = gdb_regno_caller_save(regno); + reg->reg_data_type = gdb_regno_reg_data_type(target, regno); + reg->group = gdb_regno_group(regno); + if (regno < GDB_REGNO_COUNT) { + RISCV_INFO(info); + reg->arch_info = &info->shared_reg_info; + } else { + reg->arch_info = calloc(1, sizeof(riscv_reg_info_t)); + if (!reg->arch_info) { + LOG_ERROR("Out of memory."); + return ERROR_FAIL; + } + riscv_reg_info_t * const reg_arch_info = reg->arch_info; + reg_arch_info->target = target; + reg_arch_info->custom_number = gdb_regno_custom_number(target, regno); + } + return resize_reg(target, regno, exist, gdb_regno_size(target, regno)); +} + +static int init_custom_register_names(struct list_head *expose_custom, + struct reg_name_table *custom_register_names) +{ + unsigned int custom_regs_num = 0; + if (!list_empty(expose_custom)) { + range_list_t *entry; + list_for_each_entry(entry, expose_custom, list) + custom_regs_num += entry->high - entry->low + 1; + } + + if (!custom_regs_num) + return ERROR_OK; + + custom_register_names->reg_names = calloc(custom_regs_num, sizeof(char *)); + if (!custom_register_names->reg_names) { + LOG_ERROR("Failed to allocate memory for custom_register_names->reg_names"); + return ERROR_FAIL; + } + custom_register_names->num_entries = custom_regs_num; + char **reg_names = custom_register_names->reg_names; + range_list_t *range; + unsigned int next_custom_reg_index = 0; + list_for_each_entry(range, expose_custom, list) { + for (unsigned int custom_number = range->low; custom_number <= range->high; ++custom_number) { + if (range->name) + reg_names[next_custom_reg_index] = init_reg_name(range->name); + else + reg_names[next_custom_reg_index] = + init_reg_name_with_prefix("custom", custom_number); + + if (!reg_names[next_custom_reg_index]) + return ERROR_FAIL; + ++next_custom_reg_index; + } + } + return ERROR_OK; +} + +int riscv_reg_impl_init_cache(struct target *target) +{ + RISCV_INFO(info); + + riscv_reg_free_all(target); + + target->reg_cache = calloc(1, sizeof(*target->reg_cache)); + if (!target->reg_cache) { + LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache"); + return ERROR_FAIL; + } + target->reg_cache->name = "RISC-V Registers"; + + if (init_custom_register_names(&info->expose_custom, &info->custom_register_names) != ERROR_OK) { + LOG_TARGET_ERROR(target, "init_custom_register_names failed"); + return ERROR_FAIL; + } + + target->reg_cache->num_regs = GDB_REGNO_COUNT + info->custom_register_names.num_entries; + LOG_TARGET_DEBUG(target, "create register cache for %d registers", + target->reg_cache->num_regs); + + target->reg_cache->reg_list = + calloc(target->reg_cache->num_regs, sizeof(struct reg)); + if (!target->reg_cache->reg_list) { + LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache->reg_list"); + return ERROR_FAIL; + } + return ERROR_OK; +} + +int riscv_reg_impl_expose_csrs(const struct target *target) +{ + RISCV_INFO(info); + range_list_t *entry; + list_for_each_entry(entry, &info->expose_csr, list) { + assert(entry->low <= entry->high); + assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0); + const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high; + for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low; + regno <= last_regno; ++regno) { + struct reg * const reg = riscv_reg_impl_cache_entry(target, regno); + const unsigned int csr_number = regno - GDB_REGNO_CSR0; + if (reg->exist) { + LOG_TARGET_WARNING(target, + "Not exposing CSR %d: register already exists.", + csr_number); + continue; + } + if (riscv_reg_impl_set_exist(target, regno, /*exist*/ true) != ERROR_OK) + return ERROR_FAIL; + LOG_TARGET_DEBUG(target, "Exposing additional CSR %d (name=%s)", + csr_number, reg->name); + } + } + return ERROR_OK; +} + +void riscv_reg_impl_hide_csrs(const struct target *target) +{ + RISCV_INFO(info); + range_list_t *entry; + list_for_each_entry(entry, &info->hide_csr, list) { + assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0); + const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high; + for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low; + regno <= last_regno; ++regno) { + struct reg * const reg = riscv_reg_impl_cache_entry(target, regno); + const unsigned int csr_number = regno - GDB_REGNO_CSR0; + if (!reg->exist) { + LOG_TARGET_DEBUG(target, + "Not hiding CSR %d: register does not exist.", + csr_number); + continue; + } + LOG_TARGET_DEBUG(target, "Hiding CSR %d (name=%s).", csr_number, reg->name); + reg->hidden = true; + } + } +} + +void riscv_reg_free_all(struct target *target) +{ + free_reg_names(target); + /* Free the shared structure use for most registers. */ + if (!target->reg_cache) + return; + if (target->reg_cache->reg_list) { + for (unsigned int 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); + target->reg_cache = NULL; +} + +int riscv_reg_flush_all(struct target *target) +{ + if (!target->reg_cache) + return ERROR_OK; + + LOG_TARGET_DEBUG(target, "Flushing register cache"); + + /* Writing non-GPR registers may require progbuf execution, and some GPRs + * may become dirty in the process (e.g. S0, S1). For that reason, flush + * registers in reverse order, so that GPRs are flushed last. + */ + for (unsigned int number = target->reg_cache->num_regs; number-- > 0; ) { + struct reg *reg = riscv_reg_impl_cache_entry(target, number); + if (reg->valid && reg->dirty) { + riscv_reg_t value = buf_get_u64(reg->value, 0, reg->size); + + LOG_TARGET_DEBUG(target, "%s is dirty; write back 0x%" PRIx64, + reg->name, value); + if (riscv_reg_write(target, number, value) != ERROR_OK) + return ERROR_FAIL; + } + } + LOG_TARGET_DEBUG(target, "Flush of register cache completed"); + return ERROR_OK; +} + +/** + * This function is used internally by functions that change register values. + * If `write_through` is true, it is ensured that the value of the target's + * register is set to be equal to the `value` argument. The cached value is + * updated if the register is cacheable. + * TODO: Currently `reg->get/set` is implemented in terms of + * `riscv_get/set_register`. However, the intention behind + * `riscv_get/set_register` is to work with the cache, therefore it accesses + * and modifyes register cache directly. The idea is to implement + * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and + * `reg->get/set`. + */ +static int riscv_set_or_write_register(struct target *target, + enum gdb_regno regid, riscv_reg_t value, bool write_through) +{ + RISCV_INFO(r); + assert(r); + if (r->dtm_version == DTM_DTMCS_VERSION_0_11) + return riscv011_set_register(target, regid, value); + + keep_alive(); + + if (regid == GDB_REGNO_PC) { + return riscv_set_or_write_register(target, GDB_REGNO_DPC, value, write_through); + } else if (regid == GDB_REGNO_PRIV) { + riscv_reg_t dcsr; + + if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV)); + dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V)); + return riscv_set_or_write_register(target, GDB_REGNO_DCSR, dcsr, write_through); + } + + struct reg *reg = riscv_reg_impl_cache_entry(target, regid); + assert(riscv_reg_impl_is_initialized(reg)); + + if (!reg->exist) { + LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name); + return ERROR_FAIL; + } + + if (target->state != TARGET_HALTED) { + LOG_TARGET_DEBUG(target, + "Target not halted, writing to target: %s <- 0x%" PRIx64, + reg->name, value); + return riscv013_set_register(target, regid, value); + } + + const bool need_to_write = !reg->valid || reg->dirty || + value != buf_get_u64(reg->value, 0, reg->size); + const bool cacheable = riscv_reg_impl_gdb_regno_cacheable(regid, need_to_write); + + if (!cacheable || (write_through && need_to_write)) { + LOG_TARGET_DEBUG(target, + "Writing to target: %s <- 0x%" PRIx64 " (cacheable=%s, valid=%s, dirty=%s)", + reg->name, value, cacheable ? "true" : "false", + reg->valid ? "true" : "false", + reg->dirty ? "true" : "false"); + if (riscv013_set_register(target, regid, value) != ERROR_OK) + return ERROR_FAIL; + + reg->dirty = false; + } else { + reg->dirty = need_to_write; + } + + buf_set_u64(reg->value, 0, reg->size, value); + reg->valid = cacheable; + + LOG_TARGET_DEBUG(target, + "Wrote 0x%" PRIx64 " to %s (cacheable=%s, valid=%s, dirty=%s)", + value, reg->name, cacheable ? "true" : "false", + reg->valid ? "true" : "false", + reg->dirty ? "true" : "false"); + return ERROR_OK; +} + +bool riscv_reg_cache_any_dirty(const struct target *target, int log_level) +{ + bool any_dirty = false; + + if (!target->reg_cache) + return any_dirty; + + for (unsigned int number = 0; number < target->reg_cache->num_regs; ++number) { + const struct reg * const reg = riscv_reg_impl_cache_entry(target, number); + assert(riscv_reg_impl_is_initialized(reg)); + if (reg->dirty) { + log_printf_lf(log_level, __FILE__, __LINE__, __func__, + "[%s] Register %s is dirty!", target_name(target), reg->name); + any_dirty = true; + } + } + return any_dirty; +} + +void riscv_reg_cache_invalidate_all(struct target *target) +{ + if (!target->reg_cache) + return; + + LOG_TARGET_DEBUG(target, "Invalidating register cache."); + register_cache_invalidate(target->reg_cache); +} + +/** + * This function is used to change the value of a register. The new value may + * be cached, and may not be written until the hart is resumed. + * TODO: Currently `reg->get/set` is implemented in terms of + * `riscv_get/set_register`. However, the intention behind + * `riscv_get/set_register` is to work with the cache, therefore it accesses + * and modifyes register cache directly. The idea is to implement + * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and + * `reg->get/set`. + */ +int riscv_reg_set(struct target *target, enum gdb_regno regid, + riscv_reg_t value) +{ + return riscv_set_or_write_register(target, regid, value, + /* write_through */ false); +} + +/** + * This function is used to change the value of a register. The new value may + * be cached, but it will be written to hart immediately. + * TODO: Currently `reg->get/set` is implemented in terms of + * `riscv_get/set_register`. However, the intention behind + * `riscv_get/set_register` is to work with the cache, therefore it accesses + * and modifyes register cache directly. The idea is to implement + * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and + * `reg->get/set`. + */ +int riscv_reg_write(struct target *target, enum gdb_regno regid, + riscv_reg_t value) +{ + return riscv_set_or_write_register(target, regid, value, + /* write_through */ true); +} + +/** + * This function is used to get the value of a register. If possible, the value + * in cache will be updated. + * TODO: Currently `reg->get/set` is implemented in terms of + * `riscv_get/set_register`. However, the intention behind + * `riscv_get/set_register` is to work with the cache, therefore it accesses + * and modifyes register cache directly. The idea is to implement + * `riscv_get/set_register` in terms of `riscv_reg_impl_cache_entry` and + * `reg->get/set`. + */ +int riscv_reg_get(struct target *target, riscv_reg_t *value, + enum gdb_regno regid) +{ + RISCV_INFO(r); + assert(r); + if (r->dtm_version == DTM_DTMCS_VERSION_0_11) + return riscv011_get_register(target, value, regid); + + keep_alive(); + + if (regid == GDB_REGNO_PC) + return riscv_reg_get(target, value, GDB_REGNO_DPC); + + struct reg *reg = riscv_reg_impl_cache_entry(target, regid); + assert(riscv_reg_impl_is_initialized(reg)); + if (!reg->exist) { + LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name); + return ERROR_FAIL; + } + + if (reg->valid) { + *value = buf_get_u64(reg->value, 0, reg->size); + LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64 " (cached)", reg->name, + *value); + return ERROR_OK; + } + + LOG_TARGET_DEBUG(target, "Reading %s from target", reg->name); + if (riscv013_get_register(target, value, regid) != ERROR_OK) + return ERROR_FAIL; + + buf_set_u64(reg->value, 0, reg->size, *value); + reg->valid = riscv_reg_impl_gdb_regno_cacheable(regid, /* is write? */ false) && + target->state == TARGET_HALTED; + reg->dirty = false; + + LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64, reg->name, *value); + return ERROR_OK; +} diff --git a/src/target/riscv/riscv_reg.h b/src/target/riscv/riscv_reg.h new file mode 100644 index 000000000..dc87c559b --- /dev/null +++ b/src/target/riscv/riscv_reg.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_H +#define OPENOCD_TARGET_RISCV_RISCV_REG_H + +#include "target/target.h" +#include "target/register.h" + +/** + * This file describes the register cache interface available to the RISC-V + * target. Functions declared here should be safe to use once register cache is + * completely initialized and may be used with caution during register cache + * initialization. + */ + +/** Return the name of the register by it's number in register cache. */ +const char *riscv_reg_gdb_regno_name(const struct target *target, enum gdb_regno regno); + +/** Free register cache and associated structures. */ +void riscv_reg_free_all(struct target *target); + +/** Write all dirty registers to the target. */ +int riscv_reg_flush_all(struct target *target); +/** + * Check whether there are any dirty registers in the OpenOCD's register cache. + * In addition, all dirty registers will be reported to the log using the + * supplied "log_level". + */ +bool riscv_reg_cache_any_dirty(const struct target *target, int log_level); +/** + * Invalidate all registers - forget their cached register values. + * WARNING: If a register was dirty, its walue will be silently lost! + */ +void riscv_reg_cache_invalidate_all(struct target *target); +/** + * Set the register value. For cacheable registers, only the cache is updated + * (write-back mode). + */ +int riscv_reg_set(struct target *target, enum gdb_regno i, riscv_reg_t v); +/** + * Set the register value and immediately write it to the target + * (write-through mode). + */ +int riscv_reg_write(struct target *target, enum gdb_regno i, riscv_reg_t v); +/** Get register, from the cache if it's in there. */ +int riscv_reg_get(struct target *target, riscv_reg_t *value, + enum gdb_regno r); + +#endif /* OPENOCD_TARGET_RISCV_RISCV_REG_H */ diff --git a/src/target/riscv/riscv_reg_impl.h b/src/target/riscv/riscv_reg_impl.h new file mode 100644 index 000000000..a86020252 --- /dev/null +++ b/src/target/riscv/riscv_reg_impl.h @@ -0,0 +1,222 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H +#define OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H + +#include "gdb_regs.h" +#include "riscv.h" +#include "target/target.h" +#include "target/register.h" +#include + +/** + * This file describes the helpers to use during register cache initialization + * of a RISC-V target. Each cache entry proceedes through the following stages: + * - not allocated before `riscv_reg_impl_init_cache()` + * - not initialized before the call to `riscv_reg_impl_init_cache_entry()` with appropriate regno. + * - initialized until `riscv_reg_free_all()` is called. + */ +static inline bool riscv_reg_impl_is_initialized(const struct reg *reg) +{ + assert(reg); + if (!reg->feature) { + const struct reg default_reg = {0}; + assert(!memcmp(&default_reg, reg, sizeof(*reg))); + return false; + } + assert(reg->arch_info); + assert(((riscv_reg_info_t *)reg->arch_info)->target); + assert((!reg->exist && !reg->value) || (reg->exist && reg->value)); + assert(reg->valid || !reg->dirty); + return true; +} +/** + * Initialize register cache. Note, that each specific register cache entry is + * not initialized by this function. + */ +int riscv_reg_impl_init_cache(struct target *target); + +/** Initialize register. */ +int riscv_reg_impl_init_cache_entry(struct target *target, uint32_t regno, + bool exist, const struct reg_arch_type *reg_type); + +/** + * For most registers, returns whether they exist or not. + * For some registers the "exist" bit should be set explicitly. + */ +bool riscv_reg_impl_gdb_regno_exist(const struct target *target, uint32_t regno); + +/** Mark register as existing or not. */ +int riscv_reg_impl_set_exist(const struct target *target, + uint32_t regno, bool exist); + +/** Return the entry in the register cache of the target. */ +struct reg *riscv_reg_impl_cache_entry(const struct target *target, + uint32_t number); + +/** Return the target that owns the cache entry. */ +struct target *riscv_reg_impl_get_target(const struct reg *reg); + +static inline void init_shared_reg_info(struct target *target) +{ + RISCV_INFO(info); + info->shared_reg_info.target = target; + info->shared_reg_info.custom_number = 0; +} + +/** TODO: vector register type description can be moved into `riscv013_info_t`, + * since 0.11 targets do not support access to vector registers. */ +static inline void riscv_reg_impl_init_vector_reg_type(const struct target *target) +{ + RISCV_INFO(info); + static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" }; + static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" }; + static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" }; + static struct reg_data_type type_uint64 = { .type = REG_TYPE_UINT64, .id = "uint64" }; + static struct reg_data_type type_uint128 = { .type = REG_TYPE_UINT128, .id = "uint128" }; + + /* This is roughly the XML we want: + * + * + * + * + * + * + * + * + * + * + * + * + */ + + info->vector_uint8.type = &type_uint8; + info->vector_uint8.count = riscv_vlenb(target); + 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 = riscv_vlenb(target) / 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 = riscv_vlenb(target) / 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 = riscv_vlenb(target) / 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 = riscv_vlenb(target) / 16; + info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED; + info->type_uint128_vector.id = "quads"; + info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR; + info->type_uint128_vector.reg_type_vector = &info->vector_uint128; + + info->vector_fields[0].name = "b"; + info->vector_fields[0].type = &info->type_uint8_vector; + if (riscv_vlenb(target) >= 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 (riscv_vlenb(target) >= 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 (riscv_vlenb(target) >= 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 (riscv_vlenb(target) >= 16) { + info->vector_fields[3].next = info->vector_fields + 4; + info->vector_fields[4].name = "q"; + info->vector_fields[4].type = &info->type_uint128_vector; + } else { + info->vector_fields[3].next = NULL; + } + info->vector_fields[4].next = NULL; + + info->vector_union.fields = info->vector_fields; + + info->type_vector.type = REG_TYPE_ARCH_DEFINED; + info->type_vector.id = "riscv_vector"; + info->type_vector.type_class = REG_TYPE_CLASS_UNION; + info->type_vector.reg_type_union = &info->vector_union; +} + +/** Expose additional CSRs, as specified by `riscv_info_t::expose_csr` list. */ +int riscv_reg_impl_expose_csrs(const struct target *target); + +/** Hide additional CSRs, as specified by `riscv_info_t::hide_csr` list. */ +void riscv_reg_impl_hide_csrs(const struct target *target); + +/** + * If write is true: + * return true iff we are guaranteed that the register will contain exactly + * the value we just wrote when it's read. + * If write is false: + * return true iff we are guaranteed that the register will read the same + * value in the future as the value we just read. + */ +static inline bool riscv_reg_impl_gdb_regno_cacheable(enum gdb_regno regno, + bool is_write) +{ + if (regno == GDB_REGNO_ZERO) + return !is_write; + + /* GPRs, FPRs, vector registers are just normal data stores. */ + if (regno <= GDB_REGNO_XPR31 || + (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) || + (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)) + return true; + + /* Most CSRs won't change value on us, but we can't assume it about arbitrary + * CSRs. */ + switch (regno) { + case GDB_REGNO_DPC: + case GDB_REGNO_VSTART: + case GDB_REGNO_VXSAT: + case GDB_REGNO_VXRM: + case GDB_REGNO_VLENB: + case GDB_REGNO_VL: + case GDB_REGNO_VTYPE: + case GDB_REGNO_MISA: + case GDB_REGNO_DCSR: + case GDB_REGNO_DSCRATCH0: + case GDB_REGNO_MEPC: + case GDB_REGNO_SATP: + /* + * WARL registers might not contain the value we just wrote, but + * these ones won't spontaneously change their value either. * + */ + return !is_write; + + case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */ + case GDB_REGNO_TDATA1: /* Changes value when tselect is changed. */ + case GDB_REGNO_TDATA2: /* Changes value when tselect is changed. */ + default: + return false; + } +} +#endif /* OPENOCD_TARGET_RISCV_RISCV_REG_IMPL_H */ diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c index da237ef33..98ccf4133 100644 --- a/src/target/riscv/riscv_semihosting.c +++ b/src/target/riscv/riscv_semihosting.c @@ -32,10 +32,62 @@ #include "target/target.h" #include "riscv.h" +#include "riscv_reg.h" static int riscv_semihosting_setup(struct target *target, int enable); static int riscv_semihosting_post_result(struct target *target); +static int riscv_semihosting_detect_magic_sequence(struct target *target, + const target_addr_t pc, bool *sequence_found) +{ + assert(sequence_found); + + /* The semihosting "magic" sequence must be the exact three instructions + * listed below. All these instructions, including the ebreak, must be + * uncompressed (4 bytes long). */ + const uint32_t magic[] = { + 0x01f01013, /* slli zero,zero,0x1f */ + 0x00100073, /* ebreak */ + 0x40705013 /* srai zero,zero,0x7 */ + }; + + LOG_TARGET_DEBUG(target, "Checking for RISC-V semihosting sequence " + "at PC = 0x%" TARGET_PRIxADDR, pc); + + /* Read three uncompressed instructions: + * The previous, the current one (pointed to by PC) and the next one. */ + const target_addr_t sequence_start_address = pc - 4; + for (int i = 0; i < 3; i++) { + uint8_t buf[4]; + + /* Instruction memories may not support arbitrary read size. + * Use any size that will work. */ + const target_addr_t address = sequence_start_address + (4 * i); + int result = riscv_read_by_any_size(target, address, 4, buf); + if (result != ERROR_OK) { + *sequence_found = false; + return result; + } + + /* RISC-V instruction layout in memory is always little endian, + * regardless of the endianness of the whole system. */ + const uint32_t value = le_to_h_u32(buf); + + LOG_TARGET_DEBUG(target, "compare 0x%08" PRIx32 " from 0x%" PRIx64 " against 0x%08" PRIx32, + value, address, magic[i]); + if (value != magic[i]) { + LOG_TARGET_DEBUG(target, "Not a RISC-V semihosting sequence"); + *sequence_found = false; + return ERROR_OK; + } + } + + LOG_TARGET_DEBUG(target, "RISC-V semihosting sequence found " + "at PC = 0x%" TARGET_PRIxADDR, pc); + *sequence_found = true; + return ERROR_OK; +} + /** * Initialize RISC-V semihosting. Use common ARM code. */ @@ -56,50 +108,47 @@ void riscv_semihosting_init(struct target *target) enum semihosting_result riscv_semihosting(struct target *target, int *retval) { struct semihosting *semihosting = target->semihosting; - if (!semihosting) { - LOG_DEBUG(" -> NONE (!semihosting)"); - return SEMIHOSTING_NONE; + assert(semihosting); + + riscv_reg_t pc; + int result = riscv_reg_get(target, &pc, GDB_REGNO_PC); + if (result != ERROR_OK) { + LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read PC)"); + return SEMIHOSTING_ERROR; + } + + bool sequence_found = false; + *retval = riscv_semihosting_detect_magic_sequence(target, pc, &sequence_found); + if (*retval != ERROR_OK) { + LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (during magic seq. detection)"); + return SEMIHOSTING_ERROR; } if (!semihosting->is_active) { - LOG_DEBUG(" -> NONE (!semihosting->is_active)"); + if (sequence_found) { + // If semihositing is encountered but disabled, provide an additional hint to the user. + LOG_TARGET_WARNING(target, "RISC-V semihosting call encountered in the program " + "but semihosting is disabled!"); + LOG_TARGET_WARNING(target, "The target will remain halted (PC = 0x%" TARGET_PRIxADDR ").", pc); + LOG_TARGET_WARNING(target, "Hint: Restart your debug session and enable semihosting " + "by command 'arm semihosting enable'."); + // TODO: This can be improved: The ebreak halt cause detection and riscv_semihosting() call + // can be added also to "arm semihosting enable", which would allow the user to continue + // without restart of the debug session. + } + + LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (semihosting not enabled)"); return SEMIHOSTING_NONE; } - riscv_reg_t pc; - int result = riscv_get_register(target, &pc, GDB_REGNO_PC); - if (result != ERROR_OK) - return SEMIHOSTING_ERROR; - - uint8_t tmp_buf[12]; - - /* Read three uncompressed instructions: The previous, the current one (pointed to by PC) and the next one */ - for (int i = 0; i < 3; i++) { - /* Instruction memories may not support arbitrary read size. Use any size that will work. */ - *retval = riscv_read_by_any_size(target, (pc - 4) + 4 * i, 4, tmp_buf + 4 * i); - if (*retval != ERROR_OK) - return SEMIHOSTING_ERROR; - } - - /* - * The instructions that trigger a semihosting call, - * always uncompressed, should look like: - * - * 01f01013 slli zero,zero,0x1f - * 00100073 ebreak - * 40705013 srai zero,zero,0x7 - */ - uint32_t pre = target_buffer_get_u32(target, tmp_buf); - uint32_t ebreak = target_buffer_get_u32(target, tmp_buf + 4); - uint32_t post = target_buffer_get_u32(target, tmp_buf + 8); - LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc); - - if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) { - /* Not the magic sequence defining semihosting. */ - LOG_DEBUG(" -> NONE (no magic)"); + if (!sequence_found) { + LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (no magic sequence)"); return SEMIHOSTING_NONE; } + /* Otherwise we have a semihosting call (and semihosting is enabled). + * Proceed with the handling of semihosting. */ + /* * Perform semihosting call if we are not waiting on a fileio * operation to complete. @@ -109,15 +158,17 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval) riscv_reg_t r0; riscv_reg_t r1; - result = riscv_get_register(target, &r0, GDB_REGNO_A0); + result = riscv_reg_get(target, &r0, GDB_REGNO_A0); if (result != ERROR_OK) { - LOG_DEBUG(" -> ERROR (couldn't read a0)"); + LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a0)"); + LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read a0)"); return SEMIHOSTING_ERROR; } - result = riscv_get_register(target, &r1, GDB_REGNO_A1); + result = riscv_reg_get(target, &r1, GDB_REGNO_A1); if (result != ERROR_OK) { - LOG_DEBUG(" -> ERROR (couldn't read a1)"); + LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a1)"); + LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (failed to read a1)"); return SEMIHOSTING_ERROR; } @@ -131,18 +182,20 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval) *retval = semihosting_common(target); if (*retval != ERROR_OK) { - LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op); + LOG_TARGET_ERROR(target, "Failed semihosting operation (0x%02X)", semihosting->op); + LOG_TARGET_DEBUG(target, "Semihosting outcome: ERROR (error during semihosting processing)"); return SEMIHOSTING_ERROR; } } else { /* Unknown operation number, not a semihosting call. */ - LOG_DEBUG(" -> NONE (unknown operation number)"); + LOG_TARGET_ERROR(target, "Unknown semihosting operation requested (op = 0x%x)", semihosting->op); + LOG_TARGET_DEBUG(target, "Semihosting outcome: NONE (unknown semihosting opcode)"); return SEMIHOSTING_NONE; } } /* Resume right after the EBREAK 4 bytes instruction. */ - *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4); + *retval = riscv_reg_set(target, GDB_REGNO_PC, pc + 4); if (*retval != ERROR_OK) return SEMIHOSTING_ERROR; @@ -151,11 +204,11 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval) * operation to complete. */ if (semihosting->is_resumable && !semihosting->hit_fileio) { - LOG_DEBUG(" -> HANDLED"); + LOG_TARGET_DEBUG(target, "Semihosting outcome: HANDLED"); return SEMIHOSTING_HANDLED; } - LOG_DEBUG(" -> WAITING"); + LOG_TARGET_DEBUG(target, "Semihosting outcome: WAITING"); return SEMIHOSTING_WAITING; } @@ -168,24 +221,21 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval) */ static int riscv_semihosting_setup(struct target *target, int enable) { - LOG_DEBUG("[%s] enable=%d", target_name(target), enable); + LOG_TARGET_DEBUG(target, "enable=%d", enable); struct semihosting *semihosting = target->semihosting; - if (semihosting) - semihosting->setup_time = clock(); + assert(semihosting); + semihosting->setup_time = clock(); return ERROR_OK; } static int riscv_semihosting_post_result(struct target *target) { struct semihosting *semihosting = target->semihosting; - if (!semihosting) { - /* If not enabled, silently ignored. */ - return 0; - } + assert(semihosting); - LOG_DEBUG("0x%" PRIx64, semihosting->result); - riscv_set_register(target, GDB_REGNO_A0, semihosting->result); + LOG_TARGET_DEBUG(target, "Result: 0x%" PRIx64, semihosting->result); + riscv_reg_set(target, GDB_REGNO_A0, semihosting->result); return 0; } diff --git a/src/target/riscv/startup.tcl b/src/target/riscv/startup.tcl new file mode 100644 index 000000000..28e03a407 --- /dev/null +++ b/src/target/riscv/startup.tcl @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +lappend _telnet_autocomplete_skip "riscv set_enable_virtual" +proc {riscv set_enable_virtual} on_off { + echo {DEPRECATED! use 'riscv virt2phys_mode' not 'riscv set_enable_virtual'} + foreach t [target names] { + if {[$t cget -type] ne "riscv"} { continue } + switch -- [$t riscv virt2phys_mode] { + off - + hw { + switch -- $on_off { + on {$t riscv virt2phys_mode hw} + off {$t riscv virt2phys_mode off} + } + } + sw { + if {$on_off eq "on"} { + error {Can't enable virtual while translation mode is SW} + } + } + } + } + return {} +} + +lappend _telnet_autocomplete_skip "riscv set_enable_virt2phys" +proc {riscv set_enable_virt2phys} on_off { + echo {DEPRECATED! use 'riscv virt2phys_mode' not 'riscv set_enable_virt2phys'} + foreach t [target names] { + if {[$t cget -type] ne "riscv"} { continue } + switch -- [riscv virt2phys_mode] { + off - + sw { + switch -- $on_off { + on {riscv virt2phys_mode sw} + off {riscv virt2phys_mode off} + } + } + hw { + if {$on_off eq "on"} { + error {Can't enable virt2phys while translation mode is HW} + } + } + } + } + return {} +} + +foreach mode {m s u} { + lappend _telnet_autocomplete_skip "riscv set_ebreak$mode" +} + +proc riscv {cmd args} { + tailcall "riscv $cmd" {*}$args +}