From 5754aebc49450cc0da5c8a90ebd059160d21f256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Rosenkr=C3=A4nzer?= Date: Tue, 6 May 2025 01:20:22 +0200 Subject: [PATCH] target: riscv: Sync with the RISC-V fork MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerate autogenerated debug_defines.{c,h} files from current riscv-debug-spec, sync remaining RISC-V target files with the RISC-V fork. This is based on the work of (in alphabetic order): Aleksey Lotosh Alexander Rumyantsev Anastasiya Chernikova Anatoly Parshintsev <114445139+aap-sc@users.noreply.github.com> Bernhard Rosenkränzer bluew Carsten Gosvig <40368726+cgsfv@users.noreply.github.com> cgsfv Craig Blackmore Dan Robertson Darius Rad dave-estes-syzexion <53795406+dave-estes-syzexion@users.noreply.github.com> Dmitry Ryzhov Dolu1990 Emmanuel Blot Ernie Edgar <43148441+ernie-sifive@users.noreply.github.com> Evgeniy Naydanov Farid Khaydari Gleb Gagarin Greg Savin <43152568+SiFiveGregS@users.noreply.github.com> Hang Xu Hsiangkai Jan Matyas jhjung81 <48940114+jhjung81@users.noreply.github.com> Jiuyang Liu Kaspar Schleiser Khem Raj Kirill Radkin liangzhen Liviu Ionescu Marc Schink Megan Wachs Nils Wistoff Palmer Dabbelt panciyan Parshintsev Anatoly Paul George Pavel S. Smirnov Philipp Wagner Ryan Macdonald Samuel Obuch Tarek BOCHKATI Tim Newsome Tobias Kaiser Tom Hebb Tommy Murphy wxjstz wzgpeter Xiang W zhusonghe Checkpatch-ignore MULTISTATEMENT_MACRO_USE_DO_WHILE is added to allow a macro in riscv-013.c that can't use do/while because it expands to a "case ...:" statement. Checkpatch-ignore TRAILING_SEMICOLON is added to allow a construct in riscv-013.c where a macro expands to either code (where it needs the semicolon) or a member of an enum (where it needs a comma). Checkpatch-ignore LONG_LINE_COMMENT and NEW_TYPEDEFS lines are added for the sake of the autogenerated files from riscv-debug-spec. All non-autogenerated files have been updated for checkpatch compliance. Checkpatch-ignore: LONG_LINE_COMMENT Checkpatch-ignore: NEW_TYPEDEFS Checkpatch-ignore: MULTISTATEMENT_MACRO_USE_DO_WHILE Checkpatch-ignore: TRAILING_SEMICOLON Change-Id: Ie594915a4d6e6f9d9dad6016b176ab76409a099a Signed-off-by: Bernhard Rosenkränzer Reviewed-on: https://review.openocd.org/c/openocd/+/8893 Tested-by: jenkins Reviewed-by: Evgeniy Naydanov Reviewed-by: Tomas Vanek --- src/target/riscv/Makefile.am | 18 +- src/target/riscv/asm.h | 40 - src/target/riscv/batch.c | 430 +- src/target/riscv/batch.h | 186 +- src/target/riscv/debug_defines.c | 3902 +++++++++++++++ src/target/riscv/debug_defines.h | 2599 +++++----- src/target/riscv/debug_reg_printer.c | 109 + src/target/riscv/debug_reg_printer.h | 40 + src/target/riscv/encoding.h | 255 +- src/target/riscv/field_helpers.h | 47 + src/target/riscv/gdb_regs.h | 21 +- src/target/riscv/opcodes.h | 323 +- src/target/riscv/program.c | 126 +- src/target/riscv/program.h | 59 +- src/target/riscv/riscv-011.c | 383 +- src/target/riscv/riscv-011.h | 15 + src/target/riscv/riscv-011_reg.c | 86 + src/target/riscv/riscv-011_reg.h | 19 + src/target/riscv/riscv-013.c | 6196 ++++++++++++++---------- src/target/riscv/riscv-013.h | 27 + src/target/riscv/riscv-013_reg.c | 389 ++ src/target/riscv/riscv-013_reg.h | 32 + src/target/riscv/riscv.c | 6515 +++++++++++++++++--------- src/target/riscv/riscv.h | 343 +- src/target/riscv/riscv_reg.c | 990 ++++ src/target/riscv/riscv_reg.h | 49 + src/target/riscv/riscv_reg_impl.h | 222 + src/target/riscv/riscv_semihosting.c | 156 +- src/target/riscv/startup.tcl | 55 + 29 files changed, 16775 insertions(+), 6857 deletions(-) delete mode 100644 src/target/riscv/asm.h create mode 100644 src/target/riscv/debug_defines.c create mode 100644 src/target/riscv/debug_reg_printer.c create mode 100644 src/target/riscv/debug_reg_printer.h create mode 100644 src/target/riscv/field_helpers.h create mode 100644 src/target/riscv/riscv-011.h create mode 100644 src/target/riscv/riscv-011_reg.c create mode 100644 src/target/riscv/riscv-011_reg.h create mode 100644 src/target/riscv/riscv-013.h create mode 100644 src/target/riscv/riscv-013_reg.c create mode 100644 src/target/riscv/riscv-013_reg.h create mode 100644 src/target/riscv/riscv_reg.c create mode 100644 src/target/riscv/riscv_reg.h create mode 100644 src/target/riscv/riscv_reg_impl.h create mode 100644 src/target/riscv/startup.tcl 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 +}