From b68674a1da7249c52b00b511fe0ceb20ace5ae4d Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Wed, 19 Aug 2020 13:24:56 -0700 Subject: [PATCH] Upstream tons of RISC-V changes. These are all the changes from https://github.com/riscv/riscv-openocd (approximately 91dc0c0c) made just to src/target/riscv/*. Some of the new code is disabled because it requires some other target-independent changes which I didn't want to include here. Built like this, OpenOCD passes: * All single-RV32 tests against spike. * All single-RV64 tests against spike. * Enough HiFive1 tests. (I suspect the failures are due to the test suite rotting.) * Many dual-RV32 (-rtos hwthread) against spike. * Many dual-RV64 (-rtos hwthread) against spike. I suspect this is an overall improvement compared to what's in mainline right now, and it gets me a lot closer to getting all the riscv-openocd work upstreamed. Change-Id: Ide2f80c9397400780ff6780d78a206bc6a6e2f98 Signed-off-by: Tim Newsome Reviewed-on: http://openocd.zylin.com/5821 Tested-by: jenkins Reviewed-by: Jan Matyas Reviewed-by: Antonio Borneo Reviewed-by: Karl Palsson --- doc/openocd.texi | 49 +- src/helper/log.h | 1 + src/target/riscv/asm.h | 2 + src/target/riscv/batch.c | 110 +- src/target/riscv/batch.h | 17 +- src/target/riscv/debug_defines.h | 2622 +++++++++++++++----------- src/target/riscv/encoding.h | 2122 ++++++++++++++++++--- src/target/riscv/gdb_regs.h | 26 +- src/target/riscv/opcodes.h | 44 + src/target/riscv/program.c | 29 +- src/target/riscv/program.h | 6 + src/target/riscv/riscv-011.c | 135 +- src/target/riscv/riscv-013.c | 2525 +++++++++++++++++-------- src/target/riscv/riscv.c | 2067 ++++++++++++++++---- src/target/riscv/riscv.h | 136 +- src/target/riscv/riscv_semihosting.c | 70 +- 16 files changed, 7286 insertions(+), 2675 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 90b49ee35e..dba2a0afa0 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -9714,8 +9714,31 @@ This is used to access 64-bit floating point registers on 32-bit targets. @end deffn @deffn Command {riscv set_prefer_sba} on|off -When on, prefer to use System Bus Access to access memory. When off, prefer to -use the Program Buffer to access memory. +When on, prefer to use System Bus Access to access memory. When off (default), +prefer to use the Program Buffer to access memory. +@end deffn + +@deffn Command {riscv set_enable_virtual} on|off +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. +@end deffn + +@deffn Command {riscv set_enable_virt2phys} on|off +When on (default), memory accesses are performed on physical or virtual memory +depending on the current satp configuration. When off, all memory accessses are +performed on physical memory. +@end deffn + +@deffn Command {riscv resume_order} normal|reversed +Some software assumes all harts are executing nearly continuously. Such +software may be sensitive to the order that harts are resumed in. On harts +that don't support hasel, this option allows the user to choose the order the +harts are resumed in. If you are using this option, it's probably masking a +race condition problem in your code. + +Normal order is from lowest hart index to highest. This is the default +behavior. Reversed order is from highest hart index to lowest. @end deffn @deffn Command {riscv set_ir} (@option{idcode}|@option{dtmcs}|@option{dmi}) [value] @@ -9729,6 +9752,26 @@ When utilizing version 0.11 of the RISC-V Debug Specification, and DBUS registers, respectively. @end deffn +@deffn Command {riscv use_bscan_tunnel} value +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. +@end deffn + +@deffn Command {riscv set_ebreakm} on|off +Control dcsr.ebreakm. When on (default), M-mode ebreak instructions trap to +OpenOCD. When off, they generate a breakpoint exception handled internally. +@end deffn + +@deffn Command {riscv set_ebreaks} on|off +Control dcsr.ebreaks. When on (default), S-mode ebreak instructions trap to +OpenOCD. When off, they generate a breakpoint exception handled internally. +@end deffn + +@deffn Command {riscv set_ebreaku} on|off +Control dcsr.ebreaku. When on (default), U-mode ebreak instructions trap to +OpenOCD. When off, they generate a breakpoint exception handled internally. +@end deffn + @subsection RISC-V Authentication Commands The following commands can be used to authenticate to a RISC-V system. Eg. a @@ -9752,7 +9795,7 @@ Write the 32-bit value to authdata. The following commands allow direct access to the Debug Module Interface, which can be used to interact with custom debug features. -@deffn Command {riscv dmi_read} +@deffn Command {riscv dmi_read} address Perform a 32-bit DMI read at address, returning the value. @end deffn diff --git a/src/helper/log.h b/src/helper/log.h index 0c6623f01e..f2ba0daa6f 100644 --- a/src/helper/log.h +++ b/src/helper/log.h @@ -154,6 +154,7 @@ extern int debug_level; #define ERROR_WAIT (-5) /* ERROR_TIMEOUT is already taken by winerror.h. */ #define ERROR_TIMEOUT_REACHED (-6) +#define ERROR_NOT_IMPLEMENTED (-7) #endif /* OPENOCD_HELPER_LOG_H */ diff --git a/src/target/riscv/asm.h b/src/target/riscv/asm.h index d81aa02859..6ceb8c9bd2 100644 --- a/src/target/riscv/asm.h +++ b/src/target/riscv/asm.h @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + #ifndef TARGET__RISCV__ASM_H #define TARGET__RISCV__ASM_H diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c index d041ed1192..43f2ffb8c0 100644 --- a/src/target/riscv/batch.c +++ b/src/target/riscv/batch.c @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -9,21 +11,53 @@ #define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) #define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) +#define DTM_DMI_MAX_ADDRESS_LENGTH ((1<target = target; out->allocated_scans = scans; out->idle_count = idle; - out->data_out = malloc(sizeof(*out->data_out) * (scans) * sizeof(uint64_t)); - out->data_in = malloc(sizeof(*out->data_in) * (scans) * sizeof(uint64_t)); + out->data_out = malloc(sizeof(*out->data_out) * (scans) * DMI_SCAN_BUF_SIZE); + if (!out->data_out) + goto error1; + out->data_in = malloc(sizeof(*out->data_in) * (scans) * DMI_SCAN_BUF_SIZE); + if (!out->data_in) + goto error2; out->fields = malloc(sizeof(*out->fields) * (scans)); + if (!out->fields) + goto error3; + if (bscan_tunnel_ir_width != 0) { + out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * (scans)); + if (!out->bscan_ctxt) + goto error4; + } out->last_scan = RISCV_SCAN_TYPE_INVALID; out->read_keys = malloc(sizeof(*out->read_keys) * (scans)); + if (!out->read_keys) + goto error5; 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: + return NULL; } void riscv_batch_free(struct riscv_batch *batch) @@ -31,6 +65,8 @@ void riscv_batch_free(struct riscv_batch *batch) free(batch->data_in); free(batch->data_out); free(batch->fields); + free(batch->bscan_ctxt); + free(batch->read_keys); free(batch); } @@ -51,7 +87,11 @@ int riscv_batch_run(struct riscv_batch *batch) riscv_batch_add_nop(batch); for (size_t i = 0; i < batch->used_scans; ++i) { - jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE); + if (bscan_tunnel_ir_width != 0) + riscv_add_bscan_tunneled_scan(batch->target, 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); } @@ -61,6 +101,12 @@ int riscv_batch_run(struct riscv_batch *batch) return ERROR_FAIL; } + 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) + 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); @@ -72,8 +118,8 @@ void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint 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 * sizeof(uint64_t)); - field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t)); + 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); batch->last_scan = RISCV_SCAN_TYPE_WRITE; @@ -85,35 +131,35 @@ size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address) 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 * sizeof(uint64_t)); - field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t)); + 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); batch->last_scan = RISCV_SCAN_TYPE_READ; batch->used_scans++; - /* FIXME We get the read response back on the next scan. For now I'm - * just sticking a NOP in there, but this should be coalesced away. */ - riscv_batch_add_nop(batch); - - batch->read_keys[batch->read_keys_used] = batch->used_scans - 1; + batch->read_keys[batch->read_keys_used] = batch->used_scans; return batch->read_keys_used++; } -uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key) +unsigned riscv_batch_get_dmi_read_op(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); - uint8_t *base = batch->data_in + 8 * index; - return base[0] | - ((uint64_t) base[1]) << 8 | - ((uint64_t) base[2]) << 16 | - ((uint64_t) base[3]) << 24 | - ((uint64_t) base[4]) << 32 | - ((uint64_t) base[5]) << 40 | - ((uint64_t) base[6]) << 48 | - ((uint64_t) base[7]) << 56; + uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index; + /* extract "op" field from the DMI read result */ + return (unsigned)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) +{ + assert(key < batch->read_keys_used); + size_t index = batch->read_keys[key]; + 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); } void riscv_batch_add_nop(struct riscv_batch *batch) @@ -121,8 +167,8 @@ 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 * sizeof(uint64_t)); - field->in_value = (void *)(batch->data_in + batch->used_scans * sizeof(uint64_t)); + 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); batch->last_scan = RISCV_SCAN_TYPE_NOP; @@ -151,13 +197,17 @@ void dump_field(int idle, const struct scan_field *field) log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, __PRETTY_FUNCTION__, - "%db %di %s %08x @%02x -> %s %08x @%02x", - field->num_bits, idle, - op_string[out_op], out_data, out_address, - status_string[in_op], in_data, in_address); + "%db %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__, "%db %di %s %08x @%02x -> ?", - field->num_bits, idle, op_string[out_op], out_data, out_address); + __FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %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; +} diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h index 70690a601c..9c42ba81ea 100644 --- a/src/target/riscv/batch.h +++ b/src/target/riscv/batch.h @@ -1,8 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + #ifndef TARGET__RISCV__SCANS_H #define TARGET__RISCV__SCANS_H #include "target/target.h" #include "jtag/jtag.h" +#include "riscv.h" enum riscv_scan_type { RISCV_SCAN_TYPE_INVALID, @@ -27,6 +30,11 @@ struct riscv_batch { uint8_t *data_in; struct scan_field *fields; + /* 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 + BSCAN mode, this field is unallocated and stays NULL */ + riscv_bscan_tunneled_scan_context_t *bscan_ctxt; + /* In JTAG we scan out the previous value's output when performing a * scan. This is a pain for users, so we just provide them the * illusion of not having to do this by eliding all but the last NOP. @@ -54,11 +62,16 @@ int riscv_batch_run(struct riscv_batch *batch); void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data); /* DMI reads must be handled in two parts: the first one schedules a read and - * provides a key, the second one actually obtains the value of that read .*/ + * 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 address); -uint64_t riscv_batch_get_dmi_read(struct riscv_batch *batch, size_t key); +unsigned 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); /* Scans in a NOP. */ 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 diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h index d6ddd4ff1e..cb518a8911 100644 --- a/src/target/riscv/debug_defines.h +++ b/src/target/riscv/debug_defines.h @@ -1,22 +1,27 @@ +/* + * This file is auto-generated by running 'make debug_defines.h' in + * https://github.com/riscv/riscv-debug-spec/ (30b1a97) + */ + #define DTM_IDCODE 0x01 /* -* Identifies the release version of this part. + * Identifies the release version of this part. */ #define DTM_IDCODE_VERSION_OFFSET 28 #define DTM_IDCODE_VERSION_LENGTH 4 #define DTM_IDCODE_VERSION (0xfU << DTM_IDCODE_VERSION_OFFSET) /* -* Identifies the designer's part number of this part. + * Identifies the designer's part number of this part. */ #define DTM_IDCODE_PARTNUMBER_OFFSET 12 #define DTM_IDCODE_PARTNUMBER_LENGTH 16 #define DTM_IDCODE_PARTNUMBER (0xffffU << DTM_IDCODE_PARTNUMBER_OFFSET) /* -* Identifies the designer/manufacturer of this part. Bits 6:0 must be -* bits 6:0 of the designer/manufacturer's Identification Code as -* assigned by JEDEC Standard JEP106. Bits 10:7 contain the modulo-16 -* count of the number of continuation characters (0x7f) in that same -* Identification Code. + * Identifies the designer/manufacturer of this part. Bits 6:0 must be + * bits 6:0 of the designer/manufacturer's Identification Code as + * assigned by JEDEC Standard JEP106. Bits 10:7 contain the modulo-16 + * count of the number of continuation characters (0x7f) in that same + * Identification Code. */ #define DTM_IDCODE_MANUFID_OFFSET 1 #define DTM_IDCODE_MANUFID_LENGTH 11 @@ -26,1389 +31,1892 @@ #define DTM_IDCODE_1 (0x1U << DTM_IDCODE_1_OFFSET) #define DTM_DTMCS 0x10 /* -* Writing 1 to this bit does a hard reset of the DTM, -* causing the DTM to forget about any outstanding DMI transactions. -* In general this should only be used when the Debugger has -* reason to expect that the outstanding DMI transaction will never -* complete (e.g. a reset condition caused an inflight DMI transaction to -* be cancelled). + * Writing 1 to this bit does a hard reset of the DTM, + * causing the DTM to forget about any outstanding DMI transactions, and + * returning all registers and internal state to their reset value. + * In general this should only be used when the Debugger has + * reason to expect that the outstanding DMI transaction will never + * complete (e.g. a reset condition caused an inflight DMI transaction to + * be cancelled). */ #define DTM_DTMCS_DMIHARDRESET_OFFSET 17 #define DTM_DTMCS_DMIHARDRESET_LENGTH 1 #define DTM_DTMCS_DMIHARDRESET (0x1U << DTM_DTMCS_DMIHARDRESET_OFFSET) /* -* Writing 1 to this bit clears the sticky error state -* and allows the DTM to retry or complete the previous -* transaction. + * Writing 1 to this bit clears the sticky error state, but does + * not affect outstanding DMI transactions. */ #define DTM_DTMCS_DMIRESET_OFFSET 16 #define DTM_DTMCS_DMIRESET_LENGTH 1 #define DTM_DTMCS_DMIRESET (0x1U << DTM_DTMCS_DMIRESET_OFFSET) /* -* 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 (\Fdmistat of 3). A debugger must still -* check \Fdmistat when necessary. -* -* 0: It is not necessary to enter Run-Test/Idle at all. -* -* 1: Enter Run-Test/Idle and leave it immediately. -* -* 2: Enter Run-Test/Idle and stay there for 1 cycle before leaving. -* -* And so on. + * 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. + * + * 0: It is not necessary to enter Run-Test/Idle at all. + * + * 1: Enter Run-Test/Idle and leave it immediately. + * + * 2: Enter Run-Test/Idle and stay there for 1 cycle before leaving. + * + * And so on. */ #define DTM_DTMCS_IDLE_OFFSET 12 #define DTM_DTMCS_IDLE_LENGTH 3 #define DTM_DTMCS_IDLE (0x7U << DTM_DTMCS_IDLE_OFFSET) /* -* 0: No error. -* -* 1: Reserved. Interpret the same as 2. -* -* 2: An operation failed (resulted in \Fop of 2). -* -* 3: An operation was attempted while a DMI access was still in -* progress (resulted in \Fop of 3). + * 0: No error. + * + * 1: Reserved. Interpret the same as 2. + * + * 2: An operation failed (resulted in \FdtmDmiOp of 2). + * + * 3: An operation was attempted while a DMI access was still in + * progress (resulted in \FdtmDmiOp of 3). */ #define DTM_DTMCS_DMISTAT_OFFSET 10 #define DTM_DTMCS_DMISTAT_LENGTH 2 #define DTM_DTMCS_DMISTAT (0x3U << DTM_DTMCS_DMISTAT_OFFSET) /* -* The size of \Faddress in \Rdmi. + * The size of \FdmSbaddressZeroAddress in \RdtmDmi. */ #define DTM_DTMCS_ABITS_OFFSET 4 #define DTM_DTMCS_ABITS_LENGTH 6 #define DTM_DTMCS_ABITS (0x3fU << DTM_DTMCS_ABITS_OFFSET) /* -* 0: Version described in spec version 0.11. -* -* 1: Version described in spec version 0.13 (and later?), which -* reduces the DMI data width to 32 bits. -* -* 15: Version not described in any available version of this spec. + * 0: Version described in spec version 0.11. + * + * 1: Version described in spec version 0.13. + * + * 15: Version not described in any available version of this spec. */ #define DTM_DTMCS_VERSION_OFFSET 0 #define DTM_DTMCS_VERSION_LENGTH 4 #define DTM_DTMCS_VERSION (0xfU << DTM_DTMCS_VERSION_OFFSET) #define DTM_DMI 0x11 /* -* Address used for DMI access. In Update-DR this value is used -* to access the DM over the DMI. + * Address used for DMI access. In Update-DR this value is used + * to access the DM over the DMI. */ #define DTM_DMI_ADDRESS_OFFSET 34 #define DTM_DMI_ADDRESS_LENGTH abits -#define DTM_DMI_ADDRESS (((1L<instruction_count; ++i) { - LOG_DEBUG("%p: debug_buffer[%02x] = DASM(0x%08x)", program, i, program->debug_buffer[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) return ERROR_FAIL; @@ -56,7 +58,8 @@ int riscv_program_exec(struct riscv_program *p, struct target *t) 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%08lx) [0x%08lx]", (int)i, (long)p->debug_buffer[i], (long)p->debug_buffer[i]); + LOG_ERROR("ram[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]", + (int)i, p->debug_buffer[i], p->debug_buffer[i]); return ERROR_FAIL; } @@ -79,6 +82,11 @@ int riscv_program_exec(struct riscv_program *p, struct target *t) return ERROR_OK; } +int riscv_program_sdr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int 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) { return riscv_program_insert(p, sw(d, b, offset)); @@ -94,6 +102,11 @@ int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno 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) +{ + 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) { return riscv_program_insert(p, lw(d, b, offset)); @@ -109,6 +122,18 @@ int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno 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) +{ + 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) +{ + assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095); + return riscv_program_insert(p, csrrci(d, z, csr - GDB_REGNO_CSR0)); +} + int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno csr) { assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095); diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h index 310460c281..2fa925aff7 100644 --- a/src/target/riscv/program.h +++ b/src/target/riscv/program.h @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + #ifndef TARGET__RISCV__PROGRAM_H #define TARGET__RISCV__PROGRAM_H @@ -55,14 +57,18 @@ int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_sa /* 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_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_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_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); diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c index cb7b744da5..9b5f7491b9 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /* * Support for RISC-V, debug version 0.11. This was never an officially adopted * spec, but SiFive made some silicon that uses it. @@ -204,7 +206,6 @@ typedef struct { * before the interrupt is cleared. */ unsigned int interrupt_high_delay; - bool need_strict_step; bool never_halted; } riscv011_info_t; @@ -519,6 +520,8 @@ typedef struct { static scans_t *scans_new(struct target *target, unsigned int scan_count) { scans_t *scans = malloc(sizeof(scans_t)); + if (!scans) + goto error0; scans->scan_count = scan_count; /* This code also gets called before xlen is detected. */ if (riscv_xlen(target)) @@ -527,10 +530,25 @@ static scans_t *scans_new(struct target *target, unsigned int scan_count) scans->scan_size = 2 + 128 / 8; scans->next_scan = 0; scans->in = calloc(scans->scan_size, scans->scan_count); + if (!scans->in) + goto error1; scans->out = calloc(scans->scan_size, scans->scan_count); + if (!scans->out) + goto error2; scans->field = calloc(scans->scan_count, sizeof(struct scan_field)); + if (!scans->field) + goto error3; scans->target = target; return scans; + +error3: + free(scans->out); +error2: + free(scans->in); +error1: + free(scans); +error0: + return NULL; } static scans_t *scans_delete(scans_t *scans) @@ -844,6 +862,8 @@ static int cache_write(struct target *target, unsigned int address, bool run) LOG_DEBUG("enter"); riscv011_info_t *info = get_info(target); scans_t *scans = scans_new(target, info->dramsize + 2); + if (!scans) + return ERROR_FAIL; unsigned int last = info->dramsize; for (unsigned int i = 0; i < info->dramsize; i++) { @@ -1012,7 +1032,7 @@ static int wait_for_state(struct target *target, enum target_state state) } } -static int read_csr(struct target *target, uint64_t *value, uint32_t csr) +static int read_remote_csr(struct target *target, uint64_t *value, uint32_t csr) { riscv011_info_t *info = get_info(target); cache_set32(target, 0, csrr(S0, csr)); @@ -1034,7 +1054,7 @@ static int read_csr(struct target *target, uint64_t *value, uint32_t csr) return ERROR_OK; } -static int write_csr(struct target *target, uint32_t csr, uint64_t value) +static int write_remote_csr(struct target *target, uint32_t csr, uint64_t value) { LOG_DEBUG("csr 0x%x <- 0x%" PRIx64, csr, value); cache_set_load(target, 0, S0, SLOT0); @@ -1062,7 +1082,7 @@ static int maybe_read_tselect(struct target *target) riscv011_info_t *info = get_info(target); if (info->tselect_dirty) { - int result = read_csr(target, &info->tselect, CSR_TSELECT); + int result = read_remote_csr(target, &info->tselect, CSR_TSELECT); if (result != ERROR_OK) return result; info->tselect_dirty = false; @@ -1076,7 +1096,7 @@ static int maybe_write_tselect(struct target *target) riscv011_info_t *info = get_info(target); if (!info->tselect_dirty) { - int result = write_csr(target, CSR_TSELECT, info->tselect); + int result = write_remote_csr(target, CSR_TSELECT, info->tselect); if (result != ERROR_OK) return result; info->tselect_dirty = true; @@ -1115,7 +1135,10 @@ static int execute_resume(struct target *target, bool step) } } - info->dcsr |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS | DCSR_EBREAKU; + 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 &= ~DCSR_HALT; if (step) @@ -1255,7 +1278,7 @@ static int register_write(struct target *target, unsigned int number, if (number == S0) { cache_set_load(target, 0, S0, SLOT0); - cache_set32(target, 1, csrw(S0, CSR_DSCRATCH)); + cache_set32(target, 1, csrw(S0, CSR_DSCRATCH0)); cache_set_jump(target, 2); } else if (number == S1) { cache_set_load(target, 0, S0, SLOT0); @@ -1384,25 +1407,6 @@ static int halt(struct target *target) return ERROR_OK; } -static int init_target(struct command_context *cmd_ctx, - struct target *target) -{ - LOG_DEBUG("init"); - riscv_info_t *generic_info = (riscv_info_t *) target->arch_info; - generic_info->get_register = get_register; - generic_info->set_register = set_register; - - generic_info->version_specific = calloc(1, sizeof(riscv011_info_t)); - if (!generic_info->version_specific) - return ERROR_FAIL; - - /* Assume 32-bit until we discover the real value in examine(). */ - generic_info->xlen[0] = 32; - riscv_init_registers(target); - - return ERROR_OK; -} - static void deinit_target(struct target *target) { LOG_DEBUG("riscv_deinit_target()"); @@ -1413,8 +1417,6 @@ static void deinit_target(struct target *target) static int strict_step(struct target *target, bool announce) { - riscv011_info_t *info = get_info(target); - LOG_DEBUG("enter"); struct watchpoint *watchpoint = target->watchpoints; @@ -1433,16 +1435,12 @@ static int strict_step(struct target *target, bool announce) watchpoint = watchpoint->next; } - info->need_strict_step = false; - return ERROR_OK; } static int step(struct target *target, int current, target_addr_t address, int handle_breakpoints) { - riscv011_info_t *info = get_info(target); - jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); if (!current) { @@ -1455,7 +1453,7 @@ static int step(struct target *target, int current, target_addr_t address, return result; } - if (info->need_strict_step || handle_breakpoints) { + if (handle_breakpoints) { int result = strict_step(target, true); if (result != ERROR_OK) return result; @@ -1486,7 +1484,6 @@ static int examine(struct target *target) } RISCV_INFO(r); - r->hart_count = 1; riscv011_info_t *info = get_info(target); info->addrbits = get_field(dtmcontrol, DTMCONTROL_ADDRBITS); @@ -1570,11 +1567,11 @@ static int examine(struct target *target) } LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target)); - if (read_csr(target, &r->misa[0], CSR_MISA) != ERROR_OK) { + if (read_remote_csr(target, &r->misa[0], CSR_MISA) != ERROR_OK) { const unsigned old_csr_misa = 0xf10; LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA, old_csr_misa); - if (read_csr(target, &r->misa[0], old_csr_misa) != ERROR_OK) { + if (read_remote_csr(target, &r->misa[0], old_csr_misa) != ERROR_OK) { /* Maybe this is an old core that still has $misa at the old * address. */ LOG_ERROR("Failed to read misa at 0x%x.", old_csr_misa); @@ -1606,6 +1603,8 @@ static riscv_error_t handle_halt_routine(struct target *target) riscv011_info_t *info = get_info(target); scans_t *scans = scans_new(target, 256); + if (!scans) + return RE_FAIL; /* Read all GPRs as fast as we can, because gdb is going to ask for them * anyway. Reading them one at a time is much slower. */ @@ -1634,7 +1633,7 @@ static riscv_error_t handle_halt_routine(struct target *target) scans_add_read(scans, SLOT0, false); /* Read S0 from dscratch */ - unsigned int csr[] = {CSR_DSCRATCH, CSR_DPC, CSR_DCSR}; + unsigned int csr[] = {CSR_DSCRATCH0, CSR_DPC, CSR_DCSR}; for (unsigned int i = 0; i < DIM(csr); i++) { scans_add_write32(scans, 0, csrr(S0, csr[i]), true); scans_add_read(scans, SLOT0, false); @@ -1848,9 +1847,6 @@ static int handle_halt(struct target *target, bool announce) break; case DCSR_CAUSE_HWBP: target->debug_reason = DBG_REASON_WATCHPOINT; - /* If we halted because of a data trigger, gdb doesn't know to do - * the disable-breakpoints-step-enable-breakpoints dance. */ - info->need_strict_step = true; break; case DCSR_CAUSE_DEBUGINT: target->debug_reason = DBG_REASON_DBGRQ; @@ -1935,26 +1931,10 @@ static int riscv011_poll(struct target *target) static int riscv011_resume(struct target *target, int current, target_addr_t address, int handle_breakpoints, int debug_execution) { - riscv011_info_t *info = get_info(target); - + RISCV_INFO(r); jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); - if (!current) { - if (riscv_xlen(target) > 32) { - LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.", - riscv_xlen(target)); - } - int result = register_write(target, GDB_REGNO_PC, address); - if (result != ERROR_OK) - return result; - } - - if (info->need_strict_step || handle_breakpoints) { - int result = strict_step(target, false); - if (result != ERROR_OK) - return result; - } - + r->prepped = false; return resume(target, debug_execution, false); } @@ -1973,8 +1953,11 @@ 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 |= DCSR_EBREAKM | DCSR_EBREAKH | DCSR_EBREAKS | - DCSR_EBREAKU | DCSR_HALT; + 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 |= DCSR_HALT; if (target->reset_halt) info->dcsr |= DCSR_NDRESET; else @@ -2001,8 +1984,13 @@ static int deassert_reset(struct target *target) } static int read_memory(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer) + uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) { + if (increment != size) { + LOG_ERROR("read_memory with custom increment not implemented"); + return ERROR_NOT_IMPLEMENTED; + } + jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); cache_set32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16)); @@ -2029,6 +2017,8 @@ static int read_memory(struct target *target, target_addr_t address, riscv011_info_t *info = get_info(target); const unsigned max_batch_size = 256; scans_t *scans = scans_new(target, max_batch_size); + if (!scans) + return ERROR_FAIL; uint32_t result_value = 0x777; uint32_t i = 0; @@ -2185,6 +2175,8 @@ static int write_memory(struct target *target, target_addr_t address, const unsigned max_batch_size = 256; scans_t *scans = scans_new(target, max_batch_size); + if (!scans) + return ERROR_FAIL; uint32_t result_value = 0x777; uint32_t i = 0; @@ -2304,6 +2296,26 @@ static int arch_state(struct target *target) return ERROR_OK; } +static int init_target(struct command_context *cmd_ctx, + struct target *target) +{ + LOG_DEBUG("init"); + riscv_info_t *generic_info = (riscv_info_t *)target->arch_info; + generic_info->get_register = get_register; + generic_info->set_register = set_register; + generic_info->read_memory = read_memory; + + generic_info->version_specific = calloc(1, sizeof(riscv011_info_t)); + if (!generic_info->version_specific) + return ERROR_FAIL; + + /* Assume 32-bit until we discover the real value in examine(). */ + generic_info->xlen[0] = 32; + riscv_init_registers(target); + + return ERROR_OK; +} + struct target_type riscv011_target = { .name = "riscv", @@ -2321,7 +2333,6 @@ struct target_type riscv011_target = { .assert_reset = assert_reset, .deassert_reset = deassert_reset, - .read_memory = read_memory, .write_memory = write_memory, .arch_state = arch_state, diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 2f8da5b36d..8558ba8915 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /* * Support for RISC-V, debug version 0.13, which is currently (2/4/17) the * latest draft. @@ -27,11 +29,12 @@ #include "asm.h" #include "batch.h" -#define DMI_DATA1 (DMI_DATA0 + 1) -#define DMI_PROGBUF1 (DMI_PROGBUF0 + 1) +#define DM_DATA1 (DM_DATA0 + 1) +#define DM_PROGBUF1 (DM_PROGBUF0 + 1) 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); +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); /* Implementations of the functions in riscv_info_t. */ @@ -39,12 +42,13 @@ static int riscv013_get_register(struct target *target, riscv_reg_t *value, int hid, int rid); static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value); static int riscv013_select_current_hart(struct target *target); -static int riscv013_halt_current_hart(struct target *target); -static int riscv013_resume_current_hart(struct target *target); +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_on_resume(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 index, @@ -61,7 +65,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t static int register_write_direct(struct target *target, unsigned 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 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 int riscv013_test_sba_config_reg(struct target *target, target_addr_t legal_address, @@ -89,6 +93,7 @@ static int riscv013_test_compliance(struct target *target); #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) @@ -105,12 +110,6 @@ typedef enum { DMI_STATUS_BUSY = 3 } dmi_status_t; -typedef enum { - RE_OK, - RE_FAIL, - RE_AGAIN -} riscv_error_t; - typedef enum slot { SLOT0, SLOT1, @@ -146,12 +145,20 @@ typedef enum { typedef struct { struct list_head list; int abs_chain_position; + + /* The number of harts connected to this DM. */ + int hart_count; /* 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. */ 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]; } dm013_info_t; typedef struct { @@ -160,6 +167,8 @@ typedef struct { } target_list_t; typedef struct { + /* The indexed used to address this hart in its DM. */ + unsigned index; /* Number of address bits in the dbus register. */ unsigned abits; /* Number of abstract command data registers. */ @@ -229,7 +238,7 @@ static riscv013_info_t *get_info(const struct target *target) * global list of DMs. If it's not in there, then create one and initialize it * to 0. */ -static dm013_info_t *get_dm(struct target *target) +dm013_info_t *get_dm(struct target *target) { RISCV013_INFO(info); if (info->dm) @@ -247,9 +256,13 @@ static dm013_info_t *get_dm(struct target *target) } if (!dm) { + LOG_DEBUG("[%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; + dm->hart_count = -1; INIT_LIST_HEAD(&dm->target_list); list_add(&dm->list, &dm_list); } @@ -261,6 +274,10 @@ static dm013_info_t *get_dm(struct target *target) return dm; } target_entry = calloc(1, sizeof(*target_entry)); + if (!target_entry) { + info->dm = NULL; + return NULL; + } target_entry->target = target; list_add(&target_entry->list, &dm->target_list); @@ -269,14 +286,14 @@ static dm013_info_t *get_dm(struct target *target) static uint32_t set_hartsel(uint32_t initial, uint32_t index) { - initial &= ~DMI_DMCONTROL_HARTSELLO; - initial &= ~DMI_DMCONTROL_HARTSELHI; + initial &= ~DM_DMCONTROL_HARTSELLO; + initial &= ~DM_DMCONTROL_HARTSELHI; - uint32_t index_lo = index & ((1 << DMI_DMCONTROL_HARTSELLO_LENGTH) - 1); - initial |= index_lo << DMI_DMCONTROL_HARTSELLO_OFFSET; - uint32_t index_hi = index >> DMI_DMCONTROL_HARTSELLO_LENGTH; - assert(index_hi < 1 << DMI_DMCONTROL_HARTSELHI_LENGTH); - initial |= index_hi << DMI_DMCONTROL_HARTSELHI_OFFSET; + 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; return initial; } @@ -288,52 +305,56 @@ static void decode_dmi(char *text, unsigned address, unsigned data) uint64_t mask; const char *name; } description[] = { - { DMI_DMCONTROL, DMI_DMCONTROL_HALTREQ, "haltreq" }, - { DMI_DMCONTROL, DMI_DMCONTROL_RESUMEREQ, "resumereq" }, - { DMI_DMCONTROL, DMI_DMCONTROL_HARTRESET, "hartreset" }, - { DMI_DMCONTROL, DMI_DMCONTROL_HASEL, "hasel" }, - { DMI_DMCONTROL, DMI_DMCONTROL_HARTSELHI, "hartselhi" }, - { DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO, "hartsello" }, - { DMI_DMCONTROL, DMI_DMCONTROL_NDMRESET, "ndmreset" }, - { DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE, "dmactive" }, - { DMI_DMCONTROL, DMI_DMCONTROL_ACKHAVERESET, "ackhavereset" }, - - { DMI_DMSTATUS, DMI_DMSTATUS_IMPEBREAK, "impebreak" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ALLHAVERESET, "allhavereset" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ANYHAVERESET, "anyhavereset" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ALLRESUMEACK, "allresumeack" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ANYRESUMEACK, "anyresumeack" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ALLNONEXISTENT, "allnonexistent" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ANYNONEXISTENT, "anynonexistent" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ALLUNAVAIL, "allunavail" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ANYUNAVAIL, "anyunavail" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ALLRUNNING, "allrunning" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ANYRUNNING, "anyrunning" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ALLHALTED, "allhalted" }, - { DMI_DMSTATUS, DMI_DMSTATUS_ANYHALTED, "anyhalted" }, - { DMI_DMSTATUS, DMI_DMSTATUS_AUTHENTICATED, "authenticated" }, - { DMI_DMSTATUS, DMI_DMSTATUS_AUTHBUSY, "authbusy" }, - { DMI_DMSTATUS, DMI_DMSTATUS_DEVTREEVALID, "devtreevalid" }, - { DMI_DMSTATUS, DMI_DMSTATUS_VERSION, "version" }, - - { DMI_ABSTRACTCS, DMI_ABSTRACTCS_PROGBUFSIZE, "progbufsize" }, - { DMI_ABSTRACTCS, DMI_ABSTRACTCS_BUSY, "busy" }, - { DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR, "cmderr" }, - { DMI_ABSTRACTCS, DMI_ABSTRACTCS_DATACOUNT, "datacount" }, - - { DMI_COMMAND, DMI_COMMAND_CMDTYPE, "cmdtype" }, - - { DMI_SBCS, DMI_SBCS_SBREADONADDR, "sbreadonaddr" }, - { DMI_SBCS, DMI_SBCS_SBACCESS, "sbaccess" }, - { DMI_SBCS, DMI_SBCS_SBAUTOINCREMENT, "sbautoincrement" }, - { DMI_SBCS, DMI_SBCS_SBREADONDATA, "sbreadondata" }, - { DMI_SBCS, DMI_SBCS_SBERROR, "sberror" }, - { DMI_SBCS, DMI_SBCS_SBASIZE, "sbasize" }, - { DMI_SBCS, DMI_SBCS_SBACCESS128, "sbaccess128" }, - { DMI_SBCS, DMI_SBCS_SBACCESS64, "sbaccess64" }, - { DMI_SBCS, DMI_SBCS_SBACCESS32, "sbaccess32" }, - { DMI_SBCS, DMI_SBCS_SBACCESS16, "sbaccess16" }, - { DMI_SBCS, DMI_SBCS_SBACCESS8, "sbaccess8" }, + { 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; @@ -376,10 +397,9 @@ static void dump_field(int idle, const struct scan_field *field) log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, "scan", - "%db %di %s %08x @%02x -> %s %08x @%02x", - field->num_bits, idle, - op_string[out_op], out_data, out_address, - status_string[in_op], in_data, in_address); + "%db %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]; @@ -395,6 +415,10 @@ static void dump_field(int idle, const struct scan_field *field) static void select_dmi(struct target *target) { + if (bscan_tunnel_ir_width != 0) { + select_dmi_via_bscan(target); + return; + } jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE); } @@ -404,6 +428,9 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) 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); @@ -458,6 +485,7 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, .out_value = out, .in_value = in }; + riscv_bscan_tunneled_scan_context_t bscan_ctxt; if (r->reset_delays_wait >= 0) { r->reset_delays_wait--; @@ -476,8 +504,18 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, 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); - /* Assume dbus is already selected. */ - jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE); + /* 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) @@ -489,25 +527,42 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in, 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); } -/* If dmi_busy_encountered is non-NULL, this function will use it to tell the - * caller whether DMI was ever busy during this call. */ +/** + * @param data_in The data we received from the target. + * @param dmi_op The operation to perform (read/write/nop). + * @param dmi_busy_encountered + * If non-NULL, will be updated to reflect whether DMI busy was + * encountered while executing this operation or not. + * @param address The address argument to that operation. + * @param data_out The data to send to the target. + * @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) + uint32_t data_out, int timeout_sec, bool exec, bool ensure_success) { select_dmi(target); @@ -558,34 +613,32 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in, return ERROR_FAIL; } - /* 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); - } else if (status == DMI_STATUS_SUCCESS) { - break; - } else { - LOG_ERROR("failed %s (NOP) at 0x%x, status=%d", op_name, address, - status); - return ERROR_FAIL; - } - if (time(NULL) - start > timeout_sec) - return ERROR_TIMEOUT_REACHED; - } - - if (status != DMI_STATUS_SUCCESS) { - if (status == DMI_STATUS_FAILED || !data_in) { - LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address, - status); - } else { - LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d", - op_name, address, *data_in, status); + 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) { + 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); + } + return ERROR_FAIL; + } + if (time(NULL) - start > timeout_sec) + return ERROR_TIMEOUT_REACHED; } - return ERROR_FAIL; } return ERROR_OK; @@ -593,10 +646,10 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in, 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) + uint32_t data_out, bool exec, bool ensure_success) { int result = dmi_op_timeout(target, data_in, dmi_busy_encountered, dmi_op, - address, data_out, riscv_command_timeout_sec, exec); + 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 " @@ -609,32 +662,39 @@ static int dmi_op(struct target *target, uint32_t *data_in, 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); + return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, false, true); } static int dmi_read_exec(struct target *target, uint32_t *value, uint32_t address) { - return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, true); + return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, true, true); } 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); + return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, false, true); } -static int dmi_write_exec(struct target *target, uint32_t address, uint32_t value) +static int dmi_write_exec(struct target *target, uint32_t address, + uint32_t value, bool ensure_success) { - return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, true); + return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, true, ensure_success); } int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus, bool authenticated, unsigned timeout_sec) { int result = dmi_op_timeout(target, dmstatus, NULL, DMI_OP_READ, - DMI_DMSTATUS, 0, timeout_sec, false); + DM_DMSTATUS, 0, timeout_sec, false, true); if (result != ERROR_OK) return result; - if (authenticated && !get_field(*dmstatus, DMI_DMSTATUS_AUTHENTICATED)) { + 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 (0.14), not " + "%d (dmstatus=0x%x). This error might be caused by a JTAG " + "signal issue. Try reducing the JTAG clock speed.", + get_field(*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 " "`riscv authdata_write` commands to authenticate.", *dmstatus); @@ -663,11 +723,11 @@ uint32_t abstract_register_size(unsigned width) { switch (width) { case 32: - return set_field(0, AC_ACCESS_REGISTER_SIZE, 2); + return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 2); case 64: - return set_field(0, AC_ACCESS_REGISTER_SIZE, 3); + return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 3); case 128: - return set_field(0, AC_ACCESS_REGISTER_SIZE, 4); + return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 4); default: LOG_ERROR("Unsupported register width: %d", width); return 0; @@ -679,14 +739,14 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs) RISCV013_INFO(info); time_t start = time(NULL); while (1) { - if (dmi_read(target, abstractcs, DMI_ABSTRACTCS) != ERROR_OK) + if (dmi_read(target, abstractcs, DM_ABSTRACTCS) != ERROR_OK) return ERROR_FAIL; - if (get_field(*abstractcs, DMI_ABSTRACTCS_BUSY) == 0) + if (get_field(*abstractcs, DM_ABSTRACTCS_BUSY) == 0) return ERROR_OK; if (time(NULL) - start > riscv_command_timeout_sec) { - info->cmderr = get_field(*abstractcs, DMI_ABSTRACTCS_CMDERR); + info->cmderr = get_field(*abstractcs, DM_ABSTRACTCS_CMDERR); if (info->cmderr != CMDERR_NONE) { const char *errors[8] = { "none", @@ -715,12 +775,12 @@ static int execute_abstract_command(struct target *target, uint32_t command) { RISCV013_INFO(info); if (debug_level >= LOG_LVL_DEBUG) { - switch (get_field(command, DMI_COMMAND_CMDTYPE)) { + 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_SIZE), + 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), @@ -732,17 +792,17 @@ static int execute_abstract_command(struct target *target, uint32_t command) } } - dmi_write_exec(target, DMI_COMMAND, command); + if (dmi_write_exec(target, DM_COMMAND, command, false) != ERROR_OK) + return ERROR_FAIL; uint32_t abstractcs = 0; - wait_for_idle(target, &abstractcs); + int result = wait_for_idle(target, &abstractcs); - info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR); - if (info->cmderr != 0) { + 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, DMI_ABSTRACTCS, set_field(0, DMI_ABSTRACTCS_CMDERR, - info->cmderr)); + dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); return ERROR_FAIL; } @@ -757,14 +817,14 @@ static riscv_reg_t read_abstract_arg(struct target *target, unsigned index, unsigned offset = index * size_bits / 32; switch (size_bits) { default: - LOG_ERROR("Unsupported size: %d", size_bits); + LOG_ERROR("Unsupported size: %d bits", size_bits); return ~0; case 64: - dmi_read(target, &v, DMI_DATA0 + offset + 1); + dmi_read(target, &v, DM_DATA0 + offset + 1); value |= ((uint64_t) v) << 32; /* falls through */ case 32: - dmi_read(target, &v, DMI_DATA0 + offset); + dmi_read(target, &v, DM_DATA0 + offset); value |= v; } return value; @@ -776,13 +836,13 @@ static int write_abstract_arg(struct target *target, unsigned index, unsigned offset = index * size_bits / 32; switch (size_bits) { default: - LOG_ERROR("Unsupported size: %d", size_bits); + LOG_ERROR("Unsupported size: %d bits", size_bits); return ERROR_FAIL; case 64: - dmi_write(target, DMI_DATA0 + offset + 1, value >> 32); + dmi_write(target, DM_DATA0 + offset + 1, value >> 32); /* falls through */ case 32: - dmi_write(target, DMI_DATA0 + offset, value); + dmi_write(target, DM_DATA0 + offset, value); } return ERROR_OK; } @@ -793,15 +853,17 @@ static int write_abstract_arg(struct target *target, unsigned index, static uint32_t access_register_command(struct target *target, uint32_t number, unsigned size, uint32_t flags) { - uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0); + uint32_t command = set_field(0, DM_COMMAND_CMDTYPE, 0); switch (size) { case 32: - command = set_field(command, AC_ACCESS_REGISTER_SIZE, 2); + command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 2); break; case 64: - command = set_field(command, AC_ACCESS_REGISTER_SIZE, 3); + command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3); break; default: + LOG_ERROR("%d-bit register %s not supported.", size, + gdb_regno_name(number)); assert(0); } @@ -821,6 +883,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number, assert(reg_info); command = set_field(command, AC_ACCESS_REGISTER_REGNO, 0xc000 + reg_info->custom_number); + } else { + assert(0); } command |= flags; @@ -839,6 +903,9 @@ static int register_read_abstract(struct target *target, uint64_t *value, if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && !info->abstract_read_csr_supported) return ERROR_FAIL; + /* 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, AC_ACCESS_REGISTER_TRANSFER); @@ -899,6 +966,45 @@ static int register_write_abstract(struct target *target, uint32_t number, return ERROR_OK; } +/* + * Sets the AAMSIZE field of a memory access abstract command based on + * the width (bits). + */ +static uint32_t abstract_memory_size(unsigned width) +{ + switch (width) { + case 8: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 0); + case 16: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 1); + case 32: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 2); + case 64: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 3); + case 128: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 4); + default: + LOG_ERROR("Unsupported memory width: %d", width); + return 0; + } +} + +/* + * Creates a memory access abstract command. + */ +static uint32_t access_memory_command(struct target *target, bool virtual, + unsigned width, bool postincrement, bool 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); + + return command; +} + static int examine_progbuf(struct target *target) { riscv013_info_t *info = get_info(target); @@ -942,7 +1048,7 @@ static int examine_progbuf(struct target *target) } uint32_t written; - if (dmi_read(target, &written, DMI_PROGBUF0) != ERROR_OK) + if (dmi_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, @@ -958,8 +1064,58 @@ static int examine_progbuf(struct target *target) return ERROR_OK; } +static int is_fpu_reg(uint32_t gdb_regno) +{ + return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) || + (gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) || + (gdb_regno == GDB_REGNO_CSR0 + CSR_FRM) || + (gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR); +} + +static int is_vector_reg(uint32_t 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_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) +{ + 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; + } + return ERROR_OK; +} + +static int cleanup_after_register_access(struct target *target, + uint64_t mstatus, int 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; +} + typedef enum { - SPACE_DMI_DATA, + SPACE_DM_DATA, SPACE_DMI_PROGBUF, SPACE_DMI_RAM } memory_space_t; @@ -990,6 +1146,7 @@ static int scratch_reserve(struct target *target, riscv013_info_t *info = get_info(target); + /* Option 1: See if data# registers can be used as the scratch memory */ if (info->dataaccess == 1) { /* Sign extend dataaddr. */ scratch->hart_address = info->dataaddr; @@ -1000,12 +1157,13 @@ static int scratch_reserve(struct target *target, if ((size_bytes + scratch->hart_address - info->dataaddr + 3) / 4 >= info->datasize) { - scratch->memory_space = SPACE_DMI_DATA; + scratch->memory_space = SPACE_DM_DATA; scratch->debug_address = (scratch->hart_address - info->dataaddr) / 4; return ERROR_OK; } } + /* Option 2: See if progbuf can be used as the scratch memory */ if (examine_progbuf(target) != ERROR_OK) return ERROR_FAIL; @@ -1013,13 +1171,15 @@ static int scratch_reserve(struct target *target, unsigned program_size = (program->instruction_count + 1) * 4; scratch->hart_address = (info->progbuf_address + program_size + alignment - 1) & ~(alignment - 1); - if ((size_bytes + scratch->hart_address - info->progbuf_address + 3) / 4 >= - info->progbufsize) { + if ((info->progbuf_writable == YNM_YES) && + ((size_bytes + scratch->hart_address - info->progbuf_address + 3) / 4 >= + info->progbufsize)) { scratch->memory_space = SPACE_DMI_PROGBUF; scratch->debug_address = (scratch->hart_address - info->progbuf_address) / 4; return ERROR_OK; } + /* Option 3: User-configured memory area as scratch RAM */ if (target_alloc_working_area(target, size_bytes + alignment - 1, &scratch->area) == ERROR_OK) { scratch->hart_address = (scratch->area->address + alignment - 1) & @@ -1048,26 +1208,26 @@ static int scratch_read64(struct target *target, scratch_mem_t *scratch, { uint32_t v; switch (scratch->memory_space) { - case SPACE_DMI_DATA: - if (dmi_read(target, &v, DMI_DATA0 + scratch->debug_address) != ERROR_OK) + case SPACE_DM_DATA: + if (dmi_read(target, &v, DM_DATA0 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value = v; - if (dmi_read(target, &v, DMI_DATA1 + scratch->debug_address) != ERROR_OK) + if (dmi_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, DMI_PROGBUF0 + scratch->debug_address) != ERROR_OK) + if (dmi_read(target, &v, DM_PROGBUF0 + scratch->debug_address) != ERROR_OK) return ERROR_FAIL; *value = v; - if (dmi_read(target, &v, DMI_PROGBUF1 + scratch->debug_address) != ERROR_OK) + if (dmi_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]; - if (read_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK) + uint8_t buffer[8] = {0}; + if (read_memory(target, scratch->debug_address, 4, 2, buffer, 4) != ERROR_OK) return ERROR_FAIL; *value = buffer[0] | (((uint64_t) buffer[1]) << 8) | @@ -1087,13 +1247,13 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch, uint64_t value) { switch (scratch->memory_space) { - case SPACE_DMI_DATA: - dmi_write(target, DMI_DATA0 + scratch->debug_address, value); - dmi_write(target, DMI_DATA1 + scratch->debug_address, value >> 32); + case SPACE_DM_DATA: + dmi_write(target, DM_DATA0 + scratch->debug_address, value); + dmi_write(target, DM_DATA1 + scratch->debug_address, value >> 32); break; case SPACE_DMI_PROGBUF: - dmi_write(target, DMI_PROGBUF0 + scratch->debug_address, value); - dmi_write(target, DMI_PROGBUF1 + scratch->debug_address, value >> 32); + dmi_write(target, DM_PROGBUF0 + scratch->debug_address, value); + dmi_write(target, DM_PROGBUF1 + scratch->debug_address, value >> 32); break; case SPACE_DMI_RAM: { @@ -1126,6 +1286,14 @@ static unsigned register_size(struct target *target, unsigned number) return riscv_xlen(target); } +static bool has_sufficient_progbuf(struct target *target, unsigned size) +{ + RISCV013_INFO(info); + RISCV_INFO(r); + + return info->progbufsize + r->impebreak >= size; +} + /** * Immediately write the new value to the requested register. This mechanism * bypasses any caches. @@ -1133,19 +1301,12 @@ static unsigned register_size(struct target *target, unsigned number) static int register_write_direct(struct target *target, unsigned number, uint64_t value) { - RISCV013_INFO(info); - RISCV_INFO(r); - - LOG_DEBUG("{%d} reg[0x%x] <- 0x%" PRIx64, riscv_current_hartid(target), - number, value); + LOG_DEBUG("{%d} %s <- 0x%" PRIx64, riscv_current_hartid(target), + gdb_regno_name(number), value); int result = register_write_abstract(target, number, value, register_size(target, number)); - if (result == ERROR_OK && target->reg_cache) { - struct reg *reg = &target->reg_cache->reg_list[number]; - buf_set_u64(reg->value, 0, reg->size, value); - } - if (result == ERROR_OK || info->progbufsize + r->impebreak < 2 || + if (result == ERROR_OK || !has_sufficient_progbuf(target, 2) || !riscv_is_halted(target)) return result; @@ -1156,6 +1317,10 @@ static int register_write_direct(struct target *target, unsigned number, if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; + uint64_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 && @@ -1180,6 +1345,10 @@ static int register_write_direct(struct target *target, unsigned number, 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; @@ -1189,6 +1358,15 @@ static int register_write_direct(struct target *target, unsigned number, 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 { @@ -1207,6 +1385,9 @@ static int register_write_direct(struct target *target, unsigned number, if (use_scratch) scratch_release(target, &scratch); + 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; @@ -1234,14 +1415,11 @@ static int register_read(struct target *target, uint64_t *value, uint32_t number /** Actually read registers from the target right now. */ static int register_read_direct(struct target *target, uint64_t *value, uint32_t number) { - RISCV013_INFO(info); - RISCV_INFO(r); - int result = register_read_abstract(target, value, number, register_size(target, number)); if (result != ERROR_OK && - info->progbufsize + r->impebreak >= 2 && + has_sufficient_progbuf(target, 2) && number > GDB_REGNO_XPR31) { struct riscv_program program; riscv_program_init(&program, target); @@ -1249,21 +1427,17 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t scratch_mem_t scratch; bool use_scratch = false; - uint64_t s0; + riscv_reg_t s0; if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; /* Write program to move data into s0. */ uint64_t mstatus; - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - if (register_read(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) - return ERROR_FAIL; - if ((mstatus & MSTATUS_FS) == 0) - if (register_write_direct(target, GDB_REGNO_MSTATUS, - set_field(mstatus, MSTATUS_FS, 1)) != ERROR_OK) - return ERROR_FAIL; + if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) + return ERROR_FAIL; + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { if (riscv_supports_extension(target, riscv_current_hartid(target), 'D') && riscv_xlen(target) < 64) { /* There are no instructions to move all the bits from a @@ -1289,7 +1463,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { riscv_program_csrr(&program, S0, number); } else { - LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number); + LOG_ERROR("Unsupported register: %s", gdb_regno_name(number)); return ERROR_FAIL; } @@ -1308,10 +1482,8 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t return ERROR_FAIL; } - if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - (mstatus & MSTATUS_FS) == 0) - if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != 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) @@ -1319,8 +1491,8 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t } if (result == ERROR_OK) { - LOG_DEBUG("{%d} reg[0x%x] = 0x%" PRIx64, riscv_current_hartid(target), - number, *value); + LOG_DEBUG("{%d} %s = 0x%" PRIx64, riscv_current_hartid(target), + gdb_regno_name(number), *value); } return result; @@ -1335,7 +1507,7 @@ int wait_for_authbusy(struct target *target, uint32_t *dmstatus) return ERROR_FAIL; if (dmstatus) *dmstatus = value; - if (!get_field(value, DMI_DMSTATUS_AUTHBUSY)) + 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). " @@ -1360,6 +1532,36 @@ static void deinit_target(struct target *target) info->version_specific = NULL; } +static int set_haltgroup(struct target *target, bool *supported) +{ + uint32_t write = set_field(DM_DMCS2_HGWRITE, DM_DMCS2_GROUP, target->smp); + if (dmi_write(target, DM_DMCS2, write) != ERROR_OK) + return ERROR_FAIL; + uint32_t read; + if (dmi_read(target, &read, DM_DMCS2) != ERROR_OK) + return ERROR_FAIL; + *supported = get_field(read, DM_DMCS2_GROUP) == (unsigned)target->smp; + return ERROR_OK; +} + +static int discover_vlenb(struct target *target, int hartid) +{ + 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[hartid] = 0; + return ERROR_OK; + } + r->vlenb[hartid] = vlenb; + + LOG_INFO("hart %d: Vector support with vlenb=%d", hartid, r->vlenb[hartid]); + + return ERROR_OK; +} + static int examine(struct target *target) { /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ @@ -1382,43 +1584,50 @@ static int examine(struct target *target) } 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, DMI_DMCONTROL, 0); - dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE); + dmi_write(target, DM_DMCONTROL, 0); + dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE); dm->was_reset = true; } - dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO | - DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE); + 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, DMI_DMCONTROL) != ERROR_OK) + if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) return ERROR_FAIL; - if (!get_field(dmcontrol, DMI_DMCONTROL_DMACTIVE)) { + if (!get_field(dmcontrol, DM_DMCONTROL_DMACTIVE)) { LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x", dmcontrol); return ERROR_FAIL; } + dm->hasel_supported = get_field(dmcontrol, DM_DMCONTROL_HASEL); + uint32_t dmstatus; if (dmstatus_read(target, &dmstatus, false) != ERROR_OK) return ERROR_FAIL; LOG_DEBUG("dmstatus: 0x%08x", dmstatus); - if (get_field(dmstatus, DMI_DMSTATUS_VERSION) != 2) { - LOG_ERROR("OpenOCD only supports Debug Module version 2, not %d " - "(dmstatus=0x%x)", get_field(dmstatus, DMI_DMSTATUS_VERSION), 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, DMI_DMCONTROL_HARTSELHI) << - DMI_DMCONTROL_HARTSELLO_LENGTH) | - get_field(dmcontrol, DMI_DMCONTROL_HARTSELLO); + (get_field(dmcontrol, DM_DMCONTROL_HARTSELHI) << + DM_DMCONTROL_HARTSELLO_LENGTH) | + get_field(dmcontrol, DM_DMCONTROL_HARTSELLO); info->hartsellen = 0; while (hartsel & 1) { info->hartsellen++; @@ -1427,14 +1636,14 @@ static int examine(struct target *target) LOG_DEBUG("hartsellen=%d", info->hartsellen); uint32_t hartinfo; - if (dmi_read(target, &hartinfo, DMI_HARTINFO) != ERROR_OK) + if (dmi_read(target, &hartinfo, DM_HARTINFO) != ERROR_OK) return ERROR_FAIL; - info->datasize = get_field(hartinfo, DMI_HARTINFO_DATASIZE); - info->dataaccess = get_field(hartinfo, DMI_HARTINFO_DATAACCESS); - info->dataaddr = get_field(hartinfo, DMI_HARTINFO_DATAADDR); + info->datasize = get_field(hartinfo, DM_HARTINFO_DATASIZE); + info->dataaccess = get_field(hartinfo, DM_HARTINFO_DATAACCESS); + info->dataaddr = get_field(hartinfo, DM_HARTINFO_DATAADDR); - if (!get_field(dmstatus, DMI_DMSTATUS_AUTHENTICATED)) { + if (!get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED)) { LOG_ERROR("Debugger is not authenticated to target Debug Module. " "(dmstatus=0x%x). Use `riscv authdata_read` and " "`riscv authdata_write` commands to authenticate.", dmstatus); @@ -1445,33 +1654,65 @@ static int examine(struct target *target) return ERROR_OK; } - if (dmi_read(target, &info->sbcs, DMI_SBCS) != ERROR_OK) + if (dmi_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, DMI_ABSTRACTCS) != ERROR_OK) + if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) return ERROR_FAIL; - info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); - info->progbufsize = get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); + 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); RISCV_INFO(r); - r->impebreak = get_field(dmstatus, DMI_DMSTATUS_IMPEBREAK); + r->impebreak = get_field(dmstatus, DM_DMSTATUS_IMPEBREAK); - if (info->progbufsize + r->impebreak < 2) { + if (!has_sufficient_progbuf(target, 2)) { LOG_WARNING("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); + } + + if (dm->hart_count == 0) { + LOG_ERROR("No harts found!"); + return ERROR_FAIL; + } /* Don't call any riscv_* functions until after we've counted the number of * cores and initialized registers. */ - for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) { + for (int i = 0; i < dm->hart_count; ++i) { if (!riscv_rtos_enabled(target) && i != target->coreid) continue; @@ -1479,20 +1720,9 @@ static int examine(struct target *target) 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, DMI_DMSTATUS_ANYNONEXISTENT)) - break; - r->hart_count = i + 1; - - if (get_field(s, DMI_DMSTATUS_ANYHAVERESET)) - dmi_write(target, DMI_DMCONTROL, - set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i)); - bool halted = riscv_is_halted(target); if (!halted) { - if (riscv013_halt_current_hart(target) != ERROR_OK) { + if (riscv013_halt_go(target) != ERROR_OK) { LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i); return ERROR_FAIL; } @@ -1513,6 +1743,11 @@ static int examine(struct target *target) return ERROR_FAIL; } + if (riscv_supports_extension(target, i, 'V')) { + if (discover_vlenb(target, i) != ERROR_OK) + return ERROR_FAIL; + } + /* Now init registers based on what we discovered. */ if (riscv_init_registers(target) != ERROR_OK) return ERROR_FAIL; @@ -1523,18 +1758,23 @@ static int examine(struct target *target) r->misa[i]); if (!halted) - riscv013_resume_current_hart(target); + riscv013_step_or_resume_current_hart(target, false, false); } - LOG_DEBUG("Enumerated %d harts", r->hart_count); + target_set_examined(target); - if (r->hart_count == 0) { - LOG_ERROR("No harts found!"); - return ERROR_FAIL; + if (target->smp) { + bool haltgroup_supported; + if (set_haltgroup(target, &haltgroup_supported) != ERROR_OK) + return ERROR_FAIL; + if (haltgroup_supported) + LOG_INFO("Core %d made part of halt group %d.", target->coreid, + target->smp); + else + LOG_INFO("Core %d could not be made part of halt group %d.", + target->coreid, target->smp); } - target_set_examined(target); - /* 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. */ @@ -1556,7 +1796,7 @@ int riscv013_authdata_read(struct target *target, uint32_t *value) if (wait_for_authbusy(target, NULL) != ERROR_OK) return ERROR_FAIL; - return dmi_read(target, value, DMI_AUTHDATA); + return dmi_read(target, value, DM_AUTHDATA); } int riscv013_authdata_write(struct target *target, uint32_t value) @@ -1565,16 +1805,18 @@ int riscv013_authdata_write(struct target *target, uint32_t value) if (wait_for_authbusy(target, &before) != ERROR_OK) return ERROR_FAIL; - dmi_write(target, DMI_AUTHDATA, value); + dmi_write(target, DM_AUTHDATA, value); if (wait_for_authbusy(target, &after) != ERROR_OK) return ERROR_FAIL; - if (!get_field(before, DMI_DMSTATUS_AUTHENTICATED) && - get_field(after, DMI_DMSTATUS_AUTHENTICATED)) { + if (!get_field(before, DM_DMSTATUS_AUTHENTICATED) && + get_field(after, DM_DMSTATUS_AUTHENTICATED)) { LOG_INFO("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) @@ -1586,6 +1828,183 @@ int riscv013_authdata_write(struct target *target, uint32_t value) return ERROR_OK; } +static int riscv013_hart_count(struct target *target) +{ + dm013_info_t *dm = get_dm(target); + assert(dm); + return dm->hart_count; +} + +static unsigned riscv013_data_bits(struct target *target) +{ + RISCV013_INFO(info); + /* TODO: Once there is a spec for discovering abstract commands, we can + * take those into account as well. For now we assume abstract commands + * support XLEN-wide accesses. */ + if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba) + return riscv_xlen(target); + + if (get_field(info->sbcs, DM_SBCS_SBACCESS128)) + return 128; + if (get_field(info->sbcs, DM_SBCS_SBACCESS64)) + return 64; + if (get_field(info->sbcs, DM_SBCS_SBACCESS32)) + return 32; + if (get_field(info->sbcs, DM_SBCS_SBACCESS16)) + return 16; + if (get_field(info->sbcs, DM_SBCS_SBACCESS8)) + return 8; + + return riscv_xlen(target); +} + +static int prep_for_vector_access(struct target *target, uint64_t *vtype, + uint64_t *vl, unsigned *debug_vl) +{ + RISCV_INFO(r); + /* TODO: this continuous save/restore is terrible for performance. */ + /* Write vtype and vl. */ + unsigned 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)); + return ERROR_FAIL; + } + + /* Save vtype and vl. */ + if (register_read(target, vtype, GDB_REGNO_VTYPE) != ERROR_OK) + return ERROR_FAIL; + if (register_read(target, vl, GDB_REGNO_VL) != ERROR_OK) + return ERROR_FAIL; + + if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK) + return ERROR_FAIL; + *debug_vl = DIV_ROUND_UP(r->vlenb[r->current_hartid] * 8, + riscv_xlen(target)); + if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int cleanup_after_vector_access(struct target *target, uint64_t vtype, + uint64_t vl) +{ + /* Restore vtype and vl. */ + if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK) + return ERROR_FAIL; + if (register_write_direct(target, GDB_REGNO_VL, vl) != ERROR_OK) + return ERROR_FAIL; + return ERROR_OK; +} + +static int riscv013_get_register_buf(struct target *target, + uint8_t *value, int regno) +{ + assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); + + riscv_reg_t s0; + if (register_read(target, &s0, GDB_REGNO_S0) != 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 debug_vl; + if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK) + return ERROR_FAIL; + + unsigned vnum = regno - GDB_REGNO_V0; + unsigned 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 i = 0; i < debug_vl; i++) { + /* 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 + * vector register we tried to read already. + * For other failures, we just return error because things are probably + * 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; + if (register_read_direct(target, &v, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + buf_set_u64(value, xlen * i, xlen, v); + } else { + 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) + return ERROR_FAIL; + + return result; +} + +static int riscv013_set_register_buf(struct target *target, + int regno, const uint8_t *value) +{ + assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); + + riscv_reg_t s0; + if (register_read(target, &s0, GDB_REGNO_S0) != 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 debug_vl; + if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK) + return ERROR_FAIL; + + unsigned vnum = regno - GDB_REGNO_V0; + unsigned xlen = riscv_xlen(target); + + struct riscv_program program; + riscv_program_init(&program, target); + riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true)); + int result = ERROR_OK; + for (unsigned i = 0; i < debug_vl; i++) { + if (register_write_direct(target, GDB_REGNO_S0, + buf_get_u64(value, xlen * i, xlen)) != 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) + return ERROR_FAIL; + + return result; +} + static int init_target(struct command_context *cmd_ctx, struct target *target) { @@ -1594,13 +2013,16 @@ static int init_target(struct command_context *cmd_ctx, 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->halt_current_hart = &riscv013_halt_current_hart; - generic_info->resume_current_hart = &riscv013_resume_current_hart; + 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->on_resume = &riscv013_on_resume; + 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; @@ -1614,8 +2036,11 @@ static int init_target(struct command_context *cmd_ctx, 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->test_sba_config_reg = &riscv013_test_sba_config_reg; generic_info->test_compliance = &riscv013_test_compliance; + generic_info->hart_count = &riscv013_hart_count; + generic_info->data_bits = &riscv013_data_bits; generic_info->version_specific = calloc(1, sizeof(riscv013_info_t)); if (!generic_info->version_specific) return ERROR_FAIL; @@ -1647,7 +2072,7 @@ static int assert_reset(struct target *target) select_dmi(target); - uint32_t control_base = set_field(0, DMI_DMCONTROL_DMACTIVE, 1); + uint32_t control_base = set_field(0, DM_DMCONTROL_DMACTIVE, 1); if (target->rtos) { /* There's only one target, and OpenOCD thinks each hart is a thread. @@ -1662,26 +2087,35 @@ static int assert_reset(struct target *target) continue; control = set_hartsel(control_base, i); - control = set_field(control, DMI_DMCONTROL_HALTREQ, + control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); - dmi_write(target, DMI_DMCONTROL, control); + dmi_write(target, DM_DMCONTROL, control); } /* Assert ndmreset */ - control = set_field(control, DMI_DMCONTROL_NDMRESET, 1); - dmi_write(target, DMI_DMCONTROL, control); + 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); - control = set_field(control, DMI_DMCONTROL_HALTREQ, + control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); - control = set_field(control, DMI_DMCONTROL_NDMRESET, 1); - dmi_write(target, DMI_DMCONTROL, control); + control = set_field(control, DM_DMCONTROL_NDMRESET, 1); + dmi_write(target, DM_DMCONTROL, control); } target->state = TARGET_RESET; - return ERROR_OK; + 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 ERROR_OK; } static int deassert_reset(struct target *target) @@ -1692,9 +2126,9 @@ static int deassert_reset(struct target *target) /* Clear the reset, but make sure haltreq is still set */ uint32_t control = 0; - control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); - control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1); - dmi_write(target, DMI_DMCONTROL, + control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); + control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); + dmi_write(target, DM_DMCONTROL, set_hartsel(control, r->current_hartid)); uint32_t dmstatus; @@ -1706,7 +2140,7 @@ static int deassert_reset(struct target *target) if (target->rtos) { if (!riscv_hart_enabled(target, index)) continue; - dmi_write(target, DMI_DMCONTROL, + dmi_write(target, DM_DMCONTROL, set_hartsel(control, index)); } else { index = r->current_hartid; @@ -1716,10 +2150,10 @@ static int deassert_reset(struct target *target) uint32_t expected_field; if (target->reset_halt) { operation = "halt"; - expected_field = DMI_DMSTATUS_ALLHALTED; + expected_field = DM_DMSTATUS_ALLHALTED; } else { operation = "run"; - expected_field = DMI_DMSTATUS_ALLRUNNING; + expected_field = DM_DMSTATUS_ALLRUNNING; } LOG_DEBUG("Waiting for hart %d to %s out of reset.", index, operation); while (1) { @@ -1744,11 +2178,11 @@ static int deassert_reset(struct target *target) } target->state = TARGET_HALTED; - if (get_field(dmstatus, DMI_DMSTATUS_ALLHAVERESET)) { + if (get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET)) { /* Ack reset. */ - dmi_write(target, DMI_DMCONTROL, + dmi_write(target, DM_DMCONTROL, set_hartsel(control, index) | - DMI_DMCONTROL_ACKHAVERESET); + DM_DMCONTROL_ACKHAVERESET); } if (!target->rtos) @@ -1758,33 +2192,6 @@ static int deassert_reset(struct target *target) return ERROR_OK; } -/** - * @par size in bytes - */ -static void write_to_buf(uint8_t *buffer, uint64_t value, unsigned size) -{ - switch (size) { - case 8: - buffer[7] = value >> 56; - buffer[6] = value >> 48; - buffer[5] = value >> 40; - buffer[4] = value >> 32; - /* falls through */ - case 4: - buffer[3] = value >> 24; - buffer[2] = value >> 16; - /* falls through */ - case 2: - buffer[1] = value >> 8; - /* falls through */ - case 1: - buffer[0] = value; - break; - default: - assert(false); - } -} - static int execute_fence(struct target *target) { int old_hartid = riscv_current_hartid(target); @@ -1805,6 +2212,10 @@ static int execute_fence(struct target *target) if (!riscv_hart_enabled(target, i)) continue; + if (i == old_hartid) + /* Fence already executed for this hart */ + continue; + riscv_set_current_hartid(target, i); struct riscv_program program; @@ -1830,7 +2241,21 @@ static void log_memory_access(target_addr_t address, uint64_t value, char fmt[80]; sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%0%d" PRIx64, address, read ? "read" : "write", size_bytes * 2); - value &= (((uint64_t) 0x1) << (size_bytes * 8)) - 1; + switch (size_bytes) { + case 1: + value &= 0xff; + break; + case 2: + value &= 0xffff; + break; + case 4: + value &= 0xffffffffUL; + break; + case 8: + break; + default: + assert(false); + } LOG_DEBUG(fmt, value); } @@ -1840,28 +2265,16 @@ static int read_memory_bus_word(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) { uint32_t value; - if (size > 12) { - if (dmi_read(target, &value, DMI_SBDATA3) != ERROR_OK) - return ERROR_FAIL; - write_to_buf(buffer + 12, value, 4); - log_memory_access(address + 12, value, 4, true); - } - if (size > 8) { - if (dmi_read(target, &value, DMI_SBDATA2) != ERROR_OK) - return ERROR_FAIL; - write_to_buf(buffer + 8, value, 4); - log_memory_access(address + 8, value, 4, true); - } - if (size > 4) { - if (dmi_read(target, &value, DMI_SBDATA1) != ERROR_OK) - return ERROR_FAIL; - write_to_buf(buffer + 4, value, 4); - log_memory_access(address + 4, value, 4, true); + int result; + 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); + 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); } - if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK) - return ERROR_FAIL; - write_to_buf(buffer, value, MIN(size, 4)); - log_memory_access(address, value, MIN(size, 4), true); return ERROR_OK; } @@ -1869,15 +2282,15 @@ static uint32_t sb_sbaccess(unsigned size_bytes) { switch (size_bytes) { case 1: - return set_field(0, DMI_SBCS_SBACCESS, 0); + return set_field(0, DM_SBCS_SBACCESS, 0); case 2: - return set_field(0, DMI_SBCS_SBACCESS, 1); + return set_field(0, DM_SBCS_SBACCESS, 1); case 4: - return set_field(0, DMI_SBCS_SBACCESS, 2); + return set_field(0, DM_SBCS_SBACCESS, 2); case 8: - return set_field(0, DMI_SBCS_SBACCESS, 3); + return set_field(0, DM_SBCS_SBACCESS, 3); case 16: - return set_field(0, DMI_SBCS_SBACCESS, 4); + return set_field(0, DM_SBCS_SBACCESS, 4); } assert(0); return 0; /* Make mingw happy. */ @@ -1886,15 +2299,15 @@ static uint32_t sb_sbaccess(unsigned size_bytes) static target_addr_t sb_read_address(struct target *target) { RISCV013_INFO(info); - unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE); + unsigned sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); target_addr_t address = 0; uint32_t v; if (sbasize > 32) { - dmi_read(target, &v, DMI_SBADDRESS1); + dmi_read(target, &v, DM_SBADDRESS1); address |= v; address <<= 32; } - dmi_read(target, &v, DMI_SBADDRESS0); + dmi_read(target, &v, DM_SBADDRESS0); address |= v; return address; } @@ -1902,24 +2315,24 @@ static target_addr_t sb_read_address(struct target *target) static int sb_write_address(struct target *target, target_addr_t address) { RISCV013_INFO(info); - unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE); + unsigned sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); /* There currently is no support for >64-bit addresses in OpenOCD. */ if (sbasize > 96) - dmi_write(target, DMI_SBADDRESS3, 0); + dmi_write(target, DM_SBADDRESS3, 0); if (sbasize > 64) - dmi_write(target, DMI_SBADDRESS2, 0); + dmi_write(target, DM_SBADDRESS2, 0); if (sbasize > 32) - dmi_write(target, DMI_SBADDRESS1, address >> 32); - return dmi_write(target, DMI_SBADDRESS0, address); + dmi_write(target, DM_SBADDRESS1, address >> 32); + return dmi_write(target, DM_SBADDRESS0, address); } static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs) { time_t start = time(NULL); while (1) { - if (dmi_read(target, sbcs, DMI_SBCS) != ERROR_OK) + if (dmi_read(target, sbcs, DM_SBCS) != ERROR_OK) return ERROR_FAIL; - if (!get_field(*sbcs, DMI_SBCS_SBBUSY)) + 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). " @@ -1930,9 +2343,45 @@ static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs) } } +static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t *mstatus_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; + + /* Read and save MSTATUS */ + if (register_read(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) + return ERROR_FAIL; + *mstatus_old = *mstatus; + + /* 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)); + + /* MPRV = 1 */ + *mstatus = set_field(*mstatus, MSTATUS_MPRV, 1); + + /* Write MSTATUS */ + if (*mstatus != *mstatus_old) + if (register_write_direct(target, GDB_REGNO_MSTATUS, *mstatus) != 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 size, uint32_t count, uint8_t *buffer, uint32_t increment) { + if (size != increment) { + LOG_ERROR("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; @@ -1940,29 +2389,29 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address, riscv_addr_t fin_addr = address + (count * size); uint32_t access = 0; - const int DMI_SBCS_SBSINGLEREAD_OFFSET = 20; - const uint32_t DMI_SBCS_SBSINGLEREAD = (0x1U << DMI_SBCS_SBSINGLEREAD_OFFSET); + const int DM_SBCS_SBSINGLEREAD_OFFSET = 20; + const uint32_t DM_SBCS_SBSINGLEREAD = (0x1U << DM_SBCS_SBSINGLEREAD_OFFSET); - const int DMI_SBCS_SBAUTOREAD_OFFSET = 15; - const uint32_t DMI_SBCS_SBAUTOREAD = (0x1U << DMI_SBCS_SBAUTOREAD_OFFSET); + const int DM_SBCS_SBAUTOREAD_OFFSET = 15; + 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, DMI_SBCS) != ERROR_OK) + if (dmi_read(target, &access, DM_SBCS) != ERROR_OK) return ERROR_FAIL; - dmi_write(target, DMI_SBADDRESS0, cur_addr); + dmi_write(target, DM_SBADDRESS0, cur_addr); /* size/2 matching the bit access of the spec 0.13 */ - access = set_field(access, DMI_SBCS_SBACCESS, size/2); - access = set_field(access, DMI_SBCS_SBSINGLEREAD, 1); + access = set_field(access, DM_SBCS_SBACCESS, size/2); + access = set_field(access, DM_SBCS_SBSINGLEREAD, 1); LOG_DEBUG("\r\nread_memory: sab: access: 0x%08x", access); - dmi_write(target, DMI_SBCS, access); + dmi_write(target, DM_SBCS, access); /* 3) read */ uint32_t value; - if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK) + if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) return ERROR_FAIL; LOG_DEBUG("\r\nread_memory: sab: value: 0x%08x", value); - write_to_buf(t_buffer, value, size); + buf_set_u32(t_buffer, 0, 8 * size, value); t_buffer += size; cur_addr += size; } @@ -1971,36 +2420,36 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address, /* 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, DMI_SBCS) != ERROR_OK) + if (dmi_read(target, &access, DM_SBCS) != ERROR_OK) return ERROR_FAIL; /* set current address */ - dmi_write(target, DMI_SBADDRESS0, cur_addr); + dmi_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, DMI_SBCS_SBACCESS, size/2); - access = set_field(access, DMI_SBCS_SBAUTOREAD, 1); - access = set_field(access, DMI_SBCS_SBSINGLEREAD, 1); - access = set_field(access, DMI_SBCS_SBAUTOINCREMENT, 1); + access = set_field(access, DM_SBCS_SBACCESS, 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, DMI_SBCS, access); + dmi_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); /* read */ uint32_t value; - if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK) + if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) return ERROR_FAIL; - write_to_buf(t_buffer, value, size); + buf_set_u32(t_buffer, 0, 8 * size, value); cur_addr += size; t_buffer += size; /* if we are reaching last address, we must clear autoread */ if (cur_addr == fin_addr && count != 1) { - dmi_write(target, DMI_SBCS, 0); - if (dmi_read(target, &value, DMI_SBDATA0) != ERROR_OK) + dmi_write(target, DM_SBCS, 0); + if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) return ERROR_FAIL; - write_to_buf(t_buffer, value, size); + buf_set_u32(t_buffer, 0, 8 * size, value); } } @@ -2011,21 +2460,30 @@ 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 size, uint32_t count, uint8_t *buffer, uint32_t increment) { + if (increment != size && increment != 0) { + LOG_ERROR("sba v1 reads only support increment of size or 0"); + return ERROR_NOT_IMPLEMENTED; + } + RISCV013_INFO(info); target_addr_t next_address = address; target_addr_t end_address = address + count * size; while (next_address < end_address) { - uint32_t sbcs = set_field(0, DMI_SBCS_SBREADONADDR, 1); - sbcs |= sb_sbaccess(size); - sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1); - sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, count > 1); - dmi_write(target, DMI_SBCS, sbcs); + uint32_t sbcs_write = set_field(0, DM_SBCS_SBREADONADDR, 1); + sbcs_write |= sb_sbaccess(size); + if (increment == size) + 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) + return ERROR_FAIL; /* This address write will trigger the first read. */ - sb_write_address(target, next_address); + if (sb_write_address(target, next_address) != ERROR_OK) + return ERROR_FAIL; if (info->bus_master_read_delay) { jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE); @@ -2035,35 +2493,98 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, } } + /* First value has been read, and is waiting for us to issue a DMI read + * to get it. */ + + static int sbdata[4] = {DM_SBDATA0, DM_SBDATA1, DM_SBDATA2, DM_SBDATA3}; + assert(size <= 16); + target_addr_t next_read = address - 1; for (uint32_t i = (next_address - address) / size; i < count - 1; i++) { - read_memory_bus_word(target, address + i * size, size, - buffer + i * size); + for (int j = (size - 1) / 4; j >= 0; j--) { + uint32_t value; + unsigned 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_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; + } } - sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, 0); - dmi_write(target, DMI_SBCS, sbcs); + uint32_t sbcs_read = 0; + if (count > 1) { + uint32_t value; + unsigned 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); - read_memory_bus_word(target, address + (count - 1) * size, size, - buffer + (count - 1) * size); + /* "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; - if (read_sbcs_nonbusy(target, &sbcs) != 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) + 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, + buffer + (count - 1) * size) != ERROR_OK) + return ERROR_FAIL; - if (get_field(sbcs, DMI_SBCS_SBBUSYERROR)) { + if (read_sbcs_nonbusy(target, &sbcs_read) != ERROR_OK) + return ERROR_FAIL; + } + + if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { /* We read while the target was busy. Slow down and try again. */ - dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR); + if (dmi_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; continue; } - unsigned error = get_field(sbcs, DMI_SBCS_SBERROR); + unsigned error = get_field(sbcs_read, DM_SBCS_SBERROR); if (error == 0) { next_address = end_address; } else { /* Some error indicating the bus access failed, but not because of * something we did wrong. */ - dmi_write(target, DMI_SBCS, DMI_SBCS_SBERROR); + if (dmi_write(target, DM_SBCS, DM_SBCS_SBERROR) != ERROR_OK) + return ERROR_FAIL; return ERROR_FAIL; } } @@ -2086,21 +2607,152 @@ static int batch_run(const struct target *target, struct riscv_batch *batch) return riscv_batch_run(batch); } +/* + * Performs a memory read using memory access abstract commands. The read 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 read_memory_abstract(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +{ + if (size != increment) { + LOG_ERROR("abstract command reads only support size==increment"); + return ERROR_NOT_IMPLEMENTED; + } + + int result = ERROR_OK; + + LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, + size, address); + + memset(buffer, 0, count * size); + + /* Convert the size (bytes) to width (bits) */ + unsigned width = size << 3; + if (width > 64) { + /* TODO: Add 128b support if it's ever used. Involves modifying + read/write_abstract_arg() to work on two 64b values. */ + LOG_ERROR("Unsupported size: %d bits", size); + return ERROR_FAIL; + } + + /* Create the command (physical address, postincrement, read) */ + uint32_t command = access_memory_command(target, false, width, true, false); + + /* Execute the reads */ + uint8_t *p = buffer; + bool updateaddr = true; + unsigned width32 = (width + 31) / 32 * 32; + for (uint32_t c = 0; c < count; c++) { + /* Only update the address initially and let postincrement update it */ + if (updateaddr) { + /* Set arg1 to the address: address + c * size */ + result = write_abstract_arg(target, 1, address, riscv_xlen(target)); + if (result != ERROR_OK) { + LOG_ERROR("Failed to write arg1 during read_memory_abstract()."); + return result; + } + } + + /* Execute the command */ + result = execute_abstract_command(target, command); + if (result != ERROR_OK) { + LOG_ERROR("Failed to execute command read_memory_abstract()."); + return result; + } + + /* 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); + + updateaddr = false; + p += size; + } + + return result; +} + +/* + * Performs a memory write using memory access abstract commands. The write + * 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) +{ + int result = ERROR_OK; + + LOG_DEBUG("writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, + size, address); + + /* Convert the size (bytes) to width (bits) */ + unsigned width = size << 3; + if (width > 64) { + /* TODO: Add 128b support if it's ever used. Involves modifying + read/write_abstract_arg() to work on two 64b values. */ + LOG_ERROR("Unsupported size: %d bits", width); + return ERROR_FAIL; + } + + /* Create the command (physical address, postincrement, write) */ + uint32_t command = access_memory_command(target, false, width, true, true); + + /* Execute the writes */ + const uint8_t *p = buffer; + bool updateaddr = true; + for (uint32_t c = 0; c < count; c++) { + /* Move data to arg0 */ + riscv_reg_t value = buf_get_u64(p, 0, 8 * 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; + } + + /* Only update the address initially and let postincrement update it */ + if (updateaddr) { + /* Set arg1 to the address: address + c * size */ + result = write_abstract_arg(target, 1, address, riscv_xlen(target)); + if (result != ERROR_OK) { + LOG_ERROR("Failed to write arg1 during write_memory_abstract()."); + return result; + } + } + + /* Execute the command */ + result = execute_abstract_command(target, command); + if (result != ERROR_OK) { + LOG_ERROR("Failed to execute command write_memory_abstract()."); + return result; + } + + updateaddr = false; + p += size; + } + + return result; +} + /** * Read the requested memory, taking care to execute every read exactly once, * even if cmderr=busy is encountered. */ static int read_memory_progbuf_inner(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer) + uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) { RISCV013_INFO(info); int result = ERROR_OK; - /* Write address to S0, and execute buffer. */ + /* Write address to S0. */ result = register_write_direct(target, GDB_REGNO_S0, address); if (result != ERROR_OK) - goto error; + return result; + + if (increment == 0 && + register_write_direct(target, GDB_REGNO_S2, 0) != 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); @@ -2108,32 +2760,30 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres 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; - write_to_buf(buffer, value, size); + buf_set_u64(buffer, 0, 8 * size, value); log_memory_access(address, value, size, true); return ERROR_OK; } - if (dmi_write(target, DMI_ABSTRACTAUTO, - 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET) != 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, DMI_DATA0) != ERROR_OK) + 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. */ - riscv_addr_t read_addr = address + 2 * size; - riscv_addr_t fin_addr = address + (count * size); - while (read_addr < fin_addr) { - LOG_DEBUG("read_addr=0x%" PRIx64 ", fin_addr=0x%" PRIx64, read_addr, - fin_addr); + unsigned 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: @@ -2142,15 +2792,16 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres * dm_data0 contains[read_addr-size*2] */ - LOG_DEBUG("creating burst to read from 0x%" PRIx64 - " up to 0x%" PRIx64, read_addr, fin_addr); - assert(read_addr >= address && read_addr < fin_addr); struct riscv_batch *batch = riscv_batch_alloc(target, 32, info->dmi_busy_delay + info->ac_busy_delay); + if (!batch) + return ERROR_FAIL; - size_t reads = 0; - for (riscv_addr_t addr = read_addr; addr < fin_addr; addr += size) { - riscv_batch_add_dmi_read(batch, DMI_DATA0); + unsigned reads = 0; + for (unsigned 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)) @@ -2163,19 +2814,19 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres * 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, DMI_ABSTRACTCS) != ERROR_OK) + if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) return ERROR_FAIL; - while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY)) - if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK) + while (get_field(abstractcs, DM_ABSTRACTCS_BUSY)) + if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) return ERROR_FAIL; - info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR); + info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR); - riscv_addr_t next_read_addr; + unsigned next_index; unsigned ignore_last = 0; switch (info->cmderr) { case CMDERR_NONE: LOG_DEBUG("successful (partial?) memory read"); - next_read_addr = read_addr + reads * size; + next_index = index + reads; break; case CMDERR_BUSY: LOG_DEBUG("memory read resulted in busy response"); @@ -2183,35 +2834,49 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres increase_ac_busy_delay(target); riscv013_clear_abstract_error(target); - dmi_write(target, DMI_ABSTRACTAUTO, 0); + dmi_write(target, DM_ABSTRACTAUTO, 0); - uint32_t dmi_data0; + 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, DMI_DATA0) != ERROR_OK) { + 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. */ - result = register_read_direct(target, &next_read_addr, - GDB_REGNO_S0); + 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; } - write_to_buf(buffer + next_read_addr - 2 * size - address, dmi_data0, size); - log_memory_access(next_read_addr - 2 * size, dmi_data0, size, true); + + 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 DMI_DATA0 contains the next value just as it would if no + * Now DM_DATA0 contains the next value just as it would if no * error had occurred. */ - dmi_write_exec(target, DMI_COMMAND, command); - next_read_addr += size; + dmi_write_exec(target, DM_COMMAND, command, true); + next_index++; - dmi_write(target, DMI_ABSTRACTAUTO, - 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); + dmi_write(target, DM_ABSTRACTAUTO, + 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); ignore_last = 1; @@ -2226,16 +2891,18 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres /* Now read whatever we got out of the batch. */ dmi_status_t status = DMI_STATUS_SUCCESS; - for (size_t i = 0; i < reads; i++) { - riscv_addr_t receive_addr = read_addr + (i-2) * size; - assert(receive_addr < address + size * count); - if (receive_addr < address) - continue; - if (receive_addr > next_read_addr - (3 + ignore_last) * size) + unsigned read = 0; + assert(index >= 2); + for (unsigned 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; - uint64_t dmi_out = riscv_batch_get_dmi_read(batch, i); - status = get_field(dmi_out, DTM_DMI_OP); + 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 @@ -2251,28 +2918,41 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres result = ERROR_FAIL; goto error; } - uint32_t value = get_field(dmi_out, DTM_DMI_DATA); - riscv_addr_t offset = receive_addr - address; - write_to_buf(buffer + offset, value, size); - log_memory_access(receive_addr, value, size, true); - - receive_addr += size; + 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); } - read_addr = next_read_addr; + index = next_index; riscv_batch_free(batch); } - dmi_write(target, DMI_ABSTRACTAUTO, 0); + dmi_write(target, DM_ABSTRACTAUTO, 0); if (count > 1) { /* Read the penultimate word. */ - uint32_t value; - if (dmi_read(target, &value, DMI_DATA0) != ERROR_OK) + 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; - write_to_buf(buffer + size * (count-2), value, size); - log_memory_access(address + size * (count-2), value, size, true); + 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. */ @@ -2280,23 +2960,100 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres result = register_read_direct(target, &value, GDB_REGNO_S1); if (result != ERROR_OK) goto error; - write_to_buf(buffer + size * (count-1), value, size); + 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, DMI_ABSTRACTAUTO, 0); + dmi_write(target, DM_ABSTRACTAUTO, 0); 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) +{ + uint64_t mstatus = 0; + uint64_t mstatus_old = 0; + if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) + return ERROR_FAIL; + + uint64_t s0; + + if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + /* 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); + 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 (riscv_program_ebreak(&program) != ERROR_OK) + return ERROR_FAIL; + if (riscv_program_write(&program) != ERROR_OK) + return ERROR_FAIL; + + /* Write address to S0, and execute buffer. */ + if (write_abstract_arg(target, 0, address, riscv_xlen(target)) != ERROR_OK) + return ERROR_FAIL; + 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) + return ERROR_FAIL; + + uint64_t value; + if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + buf_set_u64(buffer, 0, 8 * size, value); + log_memory_access(address, value, size, true); + + if (riscv_set_register(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; + + return ERROR_OK; +} + /** * 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 size, uint32_t count, uint8_t *buffer, uint32_t increment) { + 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; + } + int result = ERROR_OK; LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, @@ -2306,21 +3063,35 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, memset(buffer, 0, count*size); + if (execute_fence(target) != ERROR_OK) + return ERROR_FAIL; + + if (count == 1) + return read_memory_progbuf_one(target, address, size, buffer); + + uint64_t mstatus = 0; + uint64_t mstatus_old = 0; + if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) + return ERROR_FAIL; + /* s0 holds the next address to write to * s1 holds the next data value to write + * s2 is a counter in case increment is 0 */ - uint64_t s0, s1; + 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 (execute_fence(target) != ERROR_OK) + if (increment == 0 && register_read(target, &s2, GDB_REGNO_S1) != ERROR_OK) return ERROR_FAIL; /* 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_S1, GDB_REGNO_S0, 0); @@ -2331,39 +3102,48 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, 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; } - riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size); + + 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; - riscv_program_write(&program); + if (riscv_program_write(&program) != ERROR_OK) + return ERROR_FAIL; - result = read_memory_progbuf_inner(target, address, size, count, buffer); + 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 size_i = size; uint32_t count_i = 1; uint8_t *buffer_i = buffer; - for (uint32_t i = 0; i < count; i++, address_i += size_i, buffer_i += size_i) { + for (uint32_t i = 0; i < count; i++, address_i += increment, buffer_i += size) { + keep_alive(); /* 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_i, count_i, buffer_i); + 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_i, address_i); + size, address_i); - uint64_t value_i = 0; - write_to_buf(buffer_i, value_i, size_i); + buf_set_u64(buffer_i, 0, 8 * size, 0); } } result = ERROR_OK; @@ -2371,32 +3151,47 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, 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; + return result; } static int read_memory(struct target *target, target_addr_t address, - uint32_t size, uint32_t count, uint8_t *buffer) + uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) { - RISCV013_INFO(info); - if (info->progbufsize >= 2 && !riscv_prefer_sba) - return read_memory_progbuf(target, address, size, count, buffer); + if (count == 0) + return ERROR_OK; - if ((get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) || - (get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) || - (get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) || - (get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) || - (get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16)) { - if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 0) - return read_memory_bus_v0(target, address, size, count, buffer); - else if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1) - return read_memory_bus_v1(target, address, size, count, buffer); + RISCV013_INFO(info); + if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba) + return read_memory_progbuf(target, address, size, count, buffer, + increment); + + if ((get_field(info->sbcs, DM_SBCS_SBACCESS8) && size == 1) || + (get_field(info->sbcs, DM_SBCS_SBACCESS16) && size == 2) || + (get_field(info->sbcs, DM_SBCS_SBACCESS32) && size == 4) || + (get_field(info->sbcs, DM_SBCS_SBACCESS64) && size == 8) || + (get_field(info->sbcs, DM_SBCS_SBACCESS128) && size == 16)) { + if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0) + return read_memory_bus_v0(target, address, size, count, buffer, + increment); + else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1) + return read_memory_bus_v1(target, address, size, count, buffer, + increment); } - if (info->progbufsize >= 2) - return read_memory_progbuf(target, address, size, count, buffer); + if (has_sufficient_progbuf(target, 3)) + return read_memory_progbuf(target, address, size, count, buffer, + increment); - LOG_ERROR("Don't know how to read memory on this target."); - return ERROR_FAIL; + return read_memory_abstract(target, address, size, count, buffer, + increment); } static int write_memory_bus_v0(struct target *target, target_addr_t address, @@ -2405,7 +3200,7 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, /*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, DMI_SBADDRESS0, address); + dmi_write(target, DM_SBADDRESS0, address); int64_t value = 0; int64_t access = 0; riscv_addr_t offset = 0; @@ -2414,42 +3209,24 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, /* B.8 Writing Memory, single write check if we write in one go */ if (count == 1) { /* count is in bytes here */ - /* check the size */ - switch (size) { - case 1: - value = t_buffer[0]; - break; - case 2: - value = t_buffer[0] - | ((uint32_t) t_buffer[1] << 8); - break; - case 4: - value = t_buffer[0] - | ((uint32_t) t_buffer[1] << 8) - | ((uint32_t) t_buffer[2] << 16) - | ((uint32_t) t_buffer[3] << 24); - break; - default: - LOG_ERROR("unsupported access size: %d", size); - return ERROR_FAIL; - } + value = buf_get_u64(t_buffer, 0, 8 * size); access = 0; - access = set_field(access, DMI_SBCS_SBACCESS, size/2); - dmi_write(target, DMI_SBCS, access); + 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, DMI_SBDATA0, value); + dmi_write(target, DM_SBDATA0, value); return ERROR_OK; } /*B.8 Writing Memory, using autoincrement*/ access = 0; - access = set_field(access, DMI_SBCS_SBACCESS, size/2); - access = set_field(access, DMI_SBCS_SBAUTOINCREMENT, 1); + access = set_field(access, DM_SBCS_SBACCESS, size/2); + access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 1); LOG_DEBUG("\r\naccess: 0x%08" PRIx64, access); - dmi_write(target, DMI_SBCS, access); + dmi_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) { @@ -2458,31 +3235,14 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address, t_addr = address + offset; t_buffer = buffer + offset; - switch (size) { - case 1: - value = t_buffer[0]; - break; - case 2: - value = t_buffer[0] - | ((uint32_t) t_buffer[1] << 8); - break; - case 4: - value = t_buffer[0] - | ((uint32_t) t_buffer[1] << 8) - | ((uint32_t) t_buffer[2] << 16) - | ((uint32_t) t_buffer[3] << 24); - break; - default: - LOG_ERROR("unsupported access size: %d", size); - return ERROR_FAIL; - } + value = buf_get_u64(t_buffer, 0, 8 * size); LOG_DEBUG("SAB:autoincrement: expected address: 0x%08x value: 0x%08x" PRIx64, (uint32_t)t_addr, (uint32_t)value); - dmi_write(target, DMI_SBDATA0, value); + dmi_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, DMI_SBCS_SBAUTOINCREMENT, 0); - dmi_write(target, DMI_SBCS, access); + access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 0); + dmi_write(target, DM_SBCS, access); return ERROR_OK; } @@ -2492,30 +3252,47 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, { RISCV013_INFO(info); uint32_t sbcs = sb_sbaccess(size); - sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1); - dmi_write(target, DMI_SBCS, sbcs); + sbcs = set_field(sbcs, DM_SBCS_SBAUTOINCREMENT, 1); + dmi_write(target, DM_SBCS, sbcs); target_addr_t next_address = address; target_addr_t end_address = address + count * size; + int result; + sb_write_address(target, next_address); while (next_address < end_address) { + LOG_DEBUG("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); + if (!batch) + return ERROR_FAIL; + for (uint32_t i = (next_address - address) / size; i < count; i++) { const uint8_t *p = buffer + i * size; + + if (riscv_batch_available_scans(batch) < (size + 3) / 4) + break; + if (size > 12) - dmi_write(target, DMI_SBDATA3, + 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) - dmi_write(target, DMI_SBDATA2, + 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) - dmi_write(target, DMI_SBDATA1, + riscv_batch_add_dmi_write(batch, DM_SBDATA1, ((uint32_t) p[4]) | (((uint32_t) p[5]) << 8) | (((uint32_t) p[6]) << 16) | @@ -2527,37 +3304,60 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, } if (size > 1) value |= ((uint32_t) p[1]) << 8; - dmi_write(target, DMI_SBDATA0, value); + riscv_batch_add_dmi_write(batch, DM_SBDATA0, value); log_memory_access(address + i * size, value, size, false); - - if (info->bus_master_write_delay) { - jtag_add_runtest(info->bus_master_write_delay, TAP_IDLE); - if (jtag_execute_queue() != ERROR_OK) { - LOG_ERROR("Failed to scan idle sequence"); - return ERROR_FAIL; - } - } + next_address += size; } - if (read_sbcs_nonbusy(target, &sbcs) != ERROR_OK) + result = batch_run(target, batch); + riscv_batch_free(batch); + if (result != ERROR_OK) + return result; + + bool dmi_busy_encountered; + if (dmi_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ, + DM_SBCS, 0, false, false) != ERROR_OK) return ERROR_FAIL; - if (get_field(sbcs, DMI_SBCS_SBBUSYERROR)) { + time_t start = time(NULL); + bool dmi_busy = dmi_busy_encountered; + while (get_field(sbcs, DM_SBCS_SBBUSY) || dmi_busy) { + 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_op(target, &sbcs, &dmi_busy, DMI_OP_READ, + DM_SBCS, 0, false, true) != ERROR_OK) + return ERROR_FAIL; + } + + if (get_field(sbcs, DM_SBCS_SBBUSYERROR)) { /* We wrote while the target was busy. Slow down and try again. */ - dmi_write(target, DMI_SBCS, DMI_SBCS_SBBUSYERROR); - next_address = sb_read_address(target); + dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR); info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1; + } + + if (get_field(sbcs, DM_SBCS_SBBUSYERROR) || dmi_busy_encountered) { + next_address = sb_read_address(target); + if (next_address < address) { + /* This should never happen, probably buggy hardware. */ + LOG_DEBUG("unexpected system bus address 0x%" TARGET_PRIxADDR, + next_address); + return ERROR_FAIL; + } + continue; } - unsigned error = get_field(sbcs, DMI_SBCS_SBERROR); - if (error == 0) { - next_address = end_address; - } else { + unsigned error = get_field(sbcs, DM_SBCS_SBERROR); + if (error != 0) { /* Some error indicating the bus access failed, but not because of * something we did wrong. */ - dmi_write(target, DMI_SBCS, DMI_SBCS_SBERROR); + dmi_write(target, DM_SBCS, DM_SBCS_SBERROR); return ERROR_FAIL; } } @@ -2570,10 +3370,21 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, { 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) + return ERROR_FAIL; + /* s0 holds the next address to write to * s1 holds the next data value to write */ @@ -2588,6 +3399,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, /* 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); switch (size) { case 1: @@ -2599,12 +3412,17 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, 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("Unsupported size: %d", size); + LOG_ERROR("write_memory_progbuf(): Unsupported size: %d", size); result = ERROR_FAIL; goto error; } + 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); result = riscv_program_ebreak(&program); @@ -2624,6 +3442,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, 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 start = (cur_addr - address) / size; @@ -2631,27 +3451,7 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, unsigned offset = size*i; const uint8_t *t_buffer = buffer + offset; - uint32_t value; - switch (size) { - case 1: - value = t_buffer[0]; - break; - case 2: - value = t_buffer[0] - | ((uint32_t) t_buffer[1] << 8); - break; - case 4: - value = t_buffer[0] - | ((uint32_t) t_buffer[1] << 8) - | ((uint32_t) t_buffer[2] << 16) - | ((uint32_t) t_buffer[3] << 24); - break; - default: - LOG_ERROR("unsupported access size: %d", size); - riscv_batch_free(batch); - result = ERROR_FAIL; - goto error; - } + uint64_t value = buf_get_u64(t_buffer, 0, 8 * size); log_memory_access(address + offset, value, size, false); cur_addr += size; @@ -2665,12 +3465,14 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, } /* Write value. */ - dmi_write(target, DMI_DATA0, 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, 32, + GDB_REGNO_S1, riscv_xlen(target), AC_ACCESS_REGISTER_POSTEXEC | AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_WRITE); @@ -2681,12 +3483,14 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, } /* Turn on autoexec */ - dmi_write(target, DMI_ABSTRACTAUTO, - 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); + dmi_write(target, DM_ABSTRACTAUTO, + 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); setup_needed = false; } else { - riscv_batch_add_dmi_write(batch, DMI_DATA0, value); + 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; } @@ -2703,13 +3507,14 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, uint32_t abstractcs; bool dmi_busy_encountered; - if (dmi_op(target, &abstractcs, &dmi_busy_encountered, DMI_OP_READ, - DMI_ABSTRACTCS, 0, false) != ERROR_OK) + 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, DMI_ABSTRACTCS_BUSY)) - if (dmi_read(target, &abstractcs, DMI_ABSTRACTCS) != ERROR_OK) + while (get_field(abstractcs, DM_ABSTRACTCS_BUSY)) + if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK) return ERROR_FAIL; - info->cmderr = get_field(abstractcs, DMI_ABSTRACTCS_CMDERR); + 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) { @@ -2720,7 +3525,7 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, riscv013_clear_abstract_error(target); increase_ac_busy_delay(target); - dmi_write(target, DMI_ABSTRACTAUTO, 0); + dmi_write(target, DM_ABSTRACTAUTO, 0); result = register_read_direct(target, &cur_addr, GDB_REGNO_S0); if (result != ERROR_OK) goto error; @@ -2734,13 +3539,18 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, } error: - dmi_write(target, DMI_ABSTRACTAUTO, 0); + 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; @@ -2751,25 +3561,25 @@ static int write_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { RISCV013_INFO(info); - if (info->progbufsize >= 2 && !riscv_prefer_sba) + + if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba) return write_memory_progbuf(target, address, size, count, buffer); - if ((get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) || - (get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) || - (get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) || - (get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) || - (get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16)) { - if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 0) + if ((get_field(info->sbcs, DM_SBCS_SBACCESS8) && size == 1) || + (get_field(info->sbcs, DM_SBCS_SBACCESS16) && size == 2) || + (get_field(info->sbcs, DM_SBCS_SBACCESS32) && size == 4) || + (get_field(info->sbcs, DM_SBCS_SBACCESS64) && size == 8) || + (get_field(info->sbcs, DM_SBCS_SBACCESS128) && size == 16)) { + if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0) return write_memory_bus_v0(target, address, size, count, buffer); - else if (get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1) + else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1) return write_memory_bus_v1(target, address, size, count, buffer); } - if (info->progbufsize >= 2) + if (has_sufficient_progbuf(target, 3)) return write_memory_progbuf(target, address, size, count, buffer); - LOG_ERROR("Don't know how to write memory on this target."); - return ERROR_FAIL; + return write_memory_abstract(target, address, size, count, buffer); } static int arch_state(struct target *target) @@ -2785,14 +3595,12 @@ struct target_type riscv013_target = { .examine = examine, .poll = &riscv_openocd_poll, - .halt = &riscv_openocd_halt, - .resume = &riscv_openocd_resume, + .halt = &riscv_halt, .step = &riscv_openocd_step, .assert_reset = assert_reset, .deassert_reset = deassert_reset, - .read_memory = read_memory, .write_memory = write_memory, .arch_state = arch_state, @@ -2802,16 +3610,19 @@ struct target_type riscv013_target = { static int riscv013_get_register(struct target *target, riscv_reg_t *value, int hid, int rid) { - LOG_DEBUG("reading register %s on hart %d", gdb_regno_name(rid), hid); + LOG_DEBUG("[%d] reading register %s on hart %d", target->coreid, + gdb_regno_name(rid), hid); riscv_set_current_hartid(target, hid); 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("read PC from DPC: 0x%" PRIx64, *value); + LOG_DEBUG("[%d] read PC from DPC: 0x%" PRIx64, target->coreid, *value); } else if (rid == GDB_REGNO_PRIV) { uint64_t dcsr; + /* TODO: move this into riscv.c. */ result = register_read(target, &dcsr, GDB_REGNO_DCSR); *value = get_field(dcsr, CSR_DCSR_PRV); } else { @@ -2825,19 +3636,19 @@ static int riscv013_get_register(struct target *target, static int riscv013_set_register(struct target *target, int hid, int rid, uint64_t value) { - LOG_DEBUG("writing 0x%" PRIx64 " to register %s on hart %d", value, - gdb_regno_name(rid), hid); + LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s on hart %d", + target->coreid, value, gdb_regno_name(rid), hid); riscv_set_current_hartid(target, hid); if (rid <= GDB_REGNO_XPR31) { return register_write_direct(target, rid, value); } else if (rid == GDB_REGNO_PC) { - LOG_DEBUG("writing PC to DPC: 0x%" PRIx64, value); + 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(" actual DPC written: 0x%016" PRIx64, actual_value); + 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); @@ -2860,32 +3671,97 @@ static int riscv013_select_current_hart(struct target *target) RISCV_INFO(r); dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; if (r->current_hartid == dm->current_hartid) return ERROR_OK; uint32_t dmcontrol; /* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */ - if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) + if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) return ERROR_FAIL; dmcontrol = set_hartsel(dmcontrol, r->current_hartid); - int result = dmi_write(target, DMI_DMCONTROL, dmcontrol); + int result = dmi_write(target, DM_DMCONTROL, dmcontrol); dm->current_hartid = r->current_hartid; return result; } -static int riscv013_halt_current_hart(struct target *target) +/* 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) { + 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; + } + + assert(dm->hart_count); + unsigned hawindow_count = (dm->hart_count + 31) / 32; + uint32_t hawindow[hawindow_count]; + + memset(hawindow, 0, sizeof(uint32_t) * hawindow_count); + + target_list_t *entry; + unsigned total_selected = 0; + list_for_each_entry(entry, &dm->target_list, list) { + struct target *t = entry->target; + riscv_info_t *r = riscv_info(t); + riscv013_info_t *info = get_info(t); + unsigned index = info->index; + LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, r->prepped); + r->selected = r->prepped; + if (r->prepped) { + hawindow[index / 32] |= 1 << (index % 32); + r->prepped = false; + total_selected++; + } + index++; + } + + /* Don't use hasel if we only need to talk to one hart. */ + if (total_selected <= 1) { + *use_hasel = false; + return ERROR_OK; + } + + for (unsigned i = 0; i < hawindow_count; i++) { + if (dmi_write(target, DM_HAWINDOWSEL, i) != ERROR_OK) + return ERROR_FAIL; + if (dmi_write(target, DM_HAWINDOW, hawindow[i]) != ERROR_OK) + return ERROR_FAIL; + } + + *use_hasel = true; + return ERROR_OK; +} + +static int riscv013_halt_prep(struct target *target) +{ + return ERROR_OK; +} + +static int riscv013_halt_go(struct target *target) +{ + bool use_hasel = false; + if (!riscv_rtos_enabled(target)) { + if (select_prepped_harts(target, &use_hasel) != ERROR_OK) + return ERROR_FAIL; + } + RISCV_INFO(r); LOG_DEBUG("halting hart %d", r->current_hartid); - if (riscv_is_halted(target)) - LOG_ERROR("Hart %d is already halted!", r->current_hartid); /* Issue the halt command, and then wait for the current hart to halt. */ - uint32_t dmcontrol; - if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) - return ERROR_FAIL; - dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 1); - dmi_write(target, DMI_DMCONTROL, dmcontrol); + 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; @@ -2894,7 +3770,7 @@ static int riscv013_halt_current_hart(struct target *target) uint32_t dmstatus; if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; - if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) + if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) return ERROR_FAIL; LOG_ERROR("unable to halt hart %d", r->current_hartid); @@ -2903,23 +3779,43 @@ static int riscv013_halt_current_hart(struct target *target) return ERROR_FAIL; } - dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HALTREQ, 0); - dmi_write(target, DMI_DMCONTROL, dmcontrol); + dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HALTREQ, 0); + dmi_write(target, DM_DMCONTROL, dmcontrol); + + if (use_hasel) { + 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; + } + } + /* The "else" case is handled in halt_go(). */ return ERROR_OK; } -static int riscv013_resume_current_hart(struct target *target) +static int riscv013_resume_go(struct target *target) { - return riscv013_step_or_resume_current_hart(target, false); + bool use_hasel = false; + if (!riscv_rtos_enabled(target)) { + if (select_prepped_harts(target, &use_hasel) != ERROR_OK) + return ERROR_FAIL; + } + + return riscv013_step_or_resume_current_hart(target, false, use_hasel); } static int riscv013_step_current_hart(struct target *target) { - return riscv013_step_or_resume_current_hart(target, true); + return riscv013_step_or_resume_current_hart(target, true, false); } -static int riscv013_on_resume(struct target *target) +static int riscv013_resume_prep(struct target *target) { return riscv013_on_step_or_resume(target, false); } @@ -2939,16 +3835,16 @@ 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, DMI_DMSTATUS_ANYUNAVAIL)) + if (get_field(dmstatus, DM_DMSTATUS_ANYUNAVAIL)) LOG_ERROR("Hart %d is unavailable.", riscv_current_hartid(target)); - if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT)) + if (get_field(dmstatus, DM_DMSTATUS_ANYNONEXISTENT)) LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target)); - if (get_field(dmstatus, DMI_DMSTATUS_ANYHAVERESET)) { + 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 = DMI_DMCONTROL_DMACTIVE | - DMI_DMCONTROL_ACKHAVERESET; + 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 @@ -2956,10 +3852,10 @@ static bool riscv013_is_halted(struct target *target) * that it is halted again once the request goes through. */ if (target->state == TARGET_HALTED) - dmcontrol |= DMI_DMCONTROL_HALTREQ; - dmi_write(target, DMI_DMCONTROL, dmcontrol); + dmcontrol |= DM_DMCONTROL_HALTREQ; + dmi_write(target, DM_DMCONTROL, dmcontrol); } - return get_field(dmstatus, DMI_DMSTATUS_ALLHALTED); + return get_field(dmstatus, DM_DMSTATUS_ALLHALTED); } static enum riscv_halt_reason riscv013_halt_reason(struct target *target) @@ -2984,6 +3880,8 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target) case CSR_DCSR_CAUSE_DEBUGINT: case CSR_DCSR_CAUSE_HALT: return RISCV_HALT_INTERRUPT; + case CSR_DCSR_CAUSE_GROUP: + return RISCV_HALT_GROUP; } LOG_ERROR("Unknown DCSR cause field: %x", (int)get_field(dcsr, CSR_DCSR_CAUSE)); @@ -2993,20 +3891,30 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target) int riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t data) { - return dmi_write(target, DMI_PROGBUF0 + index, data); + 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) + return ERROR_FAIL; + dm->progbuf_cache[index] = data; + } else { + LOG_DEBUG("cache hit for 0x%" PRIx32 " @%d", data, index); + } + return ERROR_OK; } riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index) { uint32_t value; - dmi_read(target, &value, DMI_PROGBUF0 + index); + dmi_read(target, &value, DM_PROGBUF0 + index); return value; } int riscv013_execute_debug_buffer(struct target *target) { uint32_t run_program = 0; - run_program = set_field(run_program, AC_ACCESS_REGISTER_SIZE, 2); + run_program = set_field(run_program, AC_ACCESS_REGISTER_AARSIZE, 2); run_program = set_field(run_program, AC_ACCESS_REGISTER_POSTEXEC, 1); run_program = set_field(run_program, AC_ACCESS_REGISTER_TRANSFER, 0); run_program = set_field(run_program, AC_ACCESS_REGISTER_REGNO, 0x1000); @@ -3043,11 +3951,11 @@ static int get_max_sbaccess(struct target *target) { RISCV013_INFO(info); - uint32_t sbaccess128 = get_field(info->sbcs, DMI_SBCS_SBACCESS128); - uint32_t sbaccess64 = get_field(info->sbcs, DMI_SBCS_SBACCESS64); - uint32_t sbaccess32 = get_field(info->sbcs, DMI_SBCS_SBACCESS32); - uint32_t sbaccess16 = get_field(info->sbcs, DMI_SBCS_SBACCESS16); - uint32_t sbaccess8 = get_field(info->sbcs, DMI_SBCS_SBACCESS8); + uint32_t sbaccess128 = get_field(info->sbcs, DM_SBCS_SBACCESS128); + uint32_t sbaccess64 = get_field(info->sbcs, DM_SBCS_SBACCESS64); + uint32_t sbaccess32 = get_field(info->sbcs, DM_SBCS_SBACCESS32); + uint32_t sbaccess16 = get_field(info->sbcs, DM_SBCS_SBACCESS16); + uint32_t sbaccess8 = get_field(info->sbcs, DM_SBCS_SBACCESS8); if (sbaccess128) return 4; @@ -3067,9 +3975,9 @@ static uint32_t get_num_sbdata_regs(struct target *target) { RISCV013_INFO(info); - uint32_t sbaccess128 = get_field(info->sbcs, DMI_SBCS_SBACCESS128); - uint32_t sbaccess64 = get_field(info->sbcs, DMI_SBCS_SBACCESS64); - uint32_t sbaccess32 = get_field(info->sbcs, DMI_SBCS_SBACCESS32); + uint32_t sbaccess128 = get_field(info->sbcs, DM_SBCS_SBACCESS128); + uint32_t sbaccess64 = get_field(info->sbcs, DM_SBCS_SBACCESS64); + uint32_t sbaccess32 = get_field(info->sbcs, DM_SBCS_SBACCESS32); if (sbaccess128) return 4; @@ -3091,7 +3999,7 @@ static int riscv013_test_sba_config_reg(struct target *target, uint32_t rd_val; uint32_t sbcs_orig; - dmi_read(target, &sbcs_orig, DMI_SBCS); + dmi_read(target, &sbcs_orig, DM_SBCS); uint32_t sbcs = sbcs_orig; bool test_passed; @@ -3103,25 +4011,26 @@ static int riscv013_test_sba_config_reg(struct target *target, return ERROR_FAIL; } - if (get_field(sbcs, DMI_SBCS_SBVERSION) != 1) { + if (get_field(sbcs, DM_SBCS_SBVERSION) != 1) { LOG_ERROR("System Bus Access unsupported SBVERSION (%d). Only version 1 is supported.", - get_field(sbcs, DMI_SBCS_SBVERSION)); + get_field(sbcs, DM_SBCS_SBVERSION)); return ERROR_FAIL; } uint32_t num_sbdata_regs = get_num_sbdata_regs(target); + assert(num_sbdata_regs); uint32_t rd_buf[num_sbdata_regs]; /* Test 1: Simple write/read test */ test_passed = true; - sbcs = set_field(sbcs_orig, DMI_SBCS_SBAUTOINCREMENT, 0); - dmi_write(target, DMI_SBCS, sbcs); + sbcs = set_field(sbcs_orig, DM_SBCS_SBAUTOINCREMENT, 0); + dmi_write(target, DM_SBCS, sbcs); uint32_t test_patterns[4] = {0xdeadbeef, 0xfeedbabe, 0x12345678, 0x08675309}; for (uint32_t sbaccess = 0; sbaccess <= (uint32_t)max_sbaccess; sbaccess++) { - sbcs = set_field(sbcs, DMI_SBCS_SBACCESS, sbaccess); - dmi_write(target, DMI_SBCS, sbcs); + sbcs = set_field(sbcs, DM_SBCS_SBACCESS, sbaccess); + dmi_write(target, DM_SBCS, sbcs); uint32_t compare_mask = (sbaccess == 0) ? 0xff : (sbaccess == 1) ? 0xffff : 0xffffffff; @@ -3153,14 +4062,14 @@ static int riscv013_test_sba_config_reg(struct target *target, target_addr_t curr_addr; target_addr_t prev_addr; test_passed = true; - sbcs = set_field(sbcs_orig, DMI_SBCS_SBAUTOINCREMENT, 1); - dmi_write(target, DMI_SBCS, sbcs); + sbcs = set_field(sbcs_orig, DM_SBCS_SBAUTOINCREMENT, 1); + dmi_write(target, DM_SBCS, sbcs); for (uint32_t sbaccess = 0; sbaccess <= (uint32_t)max_sbaccess; sbaccess++) { - sbcs = set_field(sbcs, DMI_SBCS_SBACCESS, sbaccess); - dmi_write(target, DMI_SBCS, sbcs); + sbcs = set_field(sbcs, DM_SBCS_SBACCESS, sbaccess); + dmi_write(target, DM_SBCS, sbcs); - dmi_write(target, DMI_SBADDRESS0, legal_address); + dmi_write(target, DM_SBADDRESS0, legal_address); read_sbcs_nonbusy(target, &sbcs); curr_addr = legal_address; for (uint32_t i = 0; i < num_words; i++) { @@ -3172,17 +4081,17 @@ static int riscv013_test_sba_config_reg(struct target *target, test_passed = false; tests_failed++; } - dmi_write(target, DMI_SBDATA0, i); + dmi_write(target, DM_SBDATA0, i); } read_sbcs_nonbusy(target, &sbcs); - dmi_write(target, DMI_SBADDRESS0, legal_address); + dmi_write(target, DM_SBADDRESS0, legal_address); uint32_t val; - sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, 1); - dmi_write(target, DMI_SBCS, sbcs); - dmi_read(target, &val, DMI_SBDATA0); /* Dummy read to trigger first system bus read */ + sbcs = set_field(sbcs, DM_SBCS_SBREADONDATA, 1); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &val, DM_SBDATA0); /* Dummy read to trigger first system bus read */ curr_addr = legal_address; for (uint32_t i = 0; i < num_words; i++) { prev_addr = curr_addr; @@ -3193,7 +4102,7 @@ static int riscv013_test_sba_config_reg(struct target *target, test_passed = false; tests_failed++; } - dmi_read(target, &val, DMI_SBDATA0); + dmi_read(target, &val, DM_SBDATA0); read_sbcs_nonbusy(target, &sbcs); if (i != val) { LOG_ERROR("System Bus Access Test 2: Error reading auto-incremented address," @@ -3209,12 +4118,12 @@ static int riscv013_test_sba_config_reg(struct target *target, /* Test 3: Read from illegal address */ read_memory_sba_simple(target, illegal_address, rd_buf, 1, sbcs_orig); - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBERROR) == 2) { - sbcs = set_field(sbcs_orig, DMI_SBCS_SBERROR, 2); - dmi_write(target, DMI_SBCS, sbcs); - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBERROR) == 0) + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 2) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBERROR, 2); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 0) LOG_INFO("System Bus Access Test 3: Illegal address read test PASSED."); else LOG_ERROR("System Bus Access Test 3: Illegal address read test FAILED, unable to clear to 0."); @@ -3225,12 +4134,12 @@ static int riscv013_test_sba_config_reg(struct target *target, /* Test 4: Write to illegal address */ write_memory_sba_simple(target, illegal_address, test_patterns, 1, sbcs_orig); - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBERROR) == 2) { - sbcs = set_field(sbcs_orig, DMI_SBCS_SBERROR, 2); - dmi_write(target, DMI_SBCS, sbcs); - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBERROR) == 0) + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 2) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBERROR, 2); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 0) LOG_INFO("System Bus Access Test 4: Illegal address write test PASSED."); else { LOG_ERROR("System Bus Access Test 4: Illegal address write test FAILED, unable to clear to 0."); @@ -3242,21 +4151,21 @@ static int riscv013_test_sba_config_reg(struct target *target, } /* Test 5: Write with unsupported sbaccess size */ - uint32_t sbaccess128 = get_field(sbcs_orig, DMI_SBCS_SBACCESS128); + uint32_t sbaccess128 = get_field(sbcs_orig, DM_SBCS_SBACCESS128); if (sbaccess128) { LOG_INFO("System Bus Access Test 5: SBCS sbaccess error test PASSED, all sbaccess sizes supported."); } else { - sbcs = set_field(sbcs_orig, DMI_SBCS_SBACCESS, 4); + sbcs = set_field(sbcs_orig, DM_SBCS_SBACCESS, 4); write_memory_sba_simple(target, legal_address, test_patterns, 1, sbcs); - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBERROR) == 4) { - sbcs = set_field(sbcs_orig, DMI_SBCS_SBERROR, 4); - dmi_write(target, DMI_SBCS, sbcs); - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBERROR) == 0) + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 4) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBERROR, 4); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 0) LOG_INFO("System Bus Access Test 5: SBCS sbaccess error test PASSED."); else { LOG_ERROR("System Bus Access Test 5: SBCS sbaccess error test FAILED, unable to clear to 0."); @@ -3269,16 +4178,16 @@ static int riscv013_test_sba_config_reg(struct target *target, } /* Test 6: Write to misaligned address */ - sbcs = set_field(sbcs_orig, DMI_SBCS_SBACCESS, 1); + sbcs = set_field(sbcs_orig, DM_SBCS_SBACCESS, 1); write_memory_sba_simple(target, legal_address+1, test_patterns, 1, sbcs); - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBERROR) == 3) { - sbcs = set_field(sbcs_orig, DMI_SBCS_SBERROR, 3); - dmi_write(target, DMI_SBCS, sbcs); - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBERROR) == 0) + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 3) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBERROR, 3); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 0) LOG_INFO("System Bus Access Test 6: SBCS address alignment error test PASSED"); else { LOG_ERROR("System Bus Access Test 6: SBCS address alignment error test FAILED, unable to clear to 0."); @@ -3292,21 +4201,21 @@ static int riscv013_test_sba_config_reg(struct target *target, /* Test 7: Set sbbusyerror, only run this case in simulation as it is likely * impossible to hit otherwise */ if (run_sbbusyerror_test) { - sbcs = set_field(sbcs_orig, DMI_SBCS_SBREADONADDR, 1); - dmi_write(target, DMI_SBCS, sbcs); + sbcs = set_field(sbcs_orig, DM_SBCS_SBREADONADDR, 1); + dmi_write(target, DM_SBCS, sbcs); for (int i = 0; i < 16; i++) - dmi_write(target, DMI_SBDATA0, 0xdeadbeef); + dmi_write(target, DM_SBDATA0, 0xdeadbeef); for (int i = 0; i < 16; i++) - dmi_write(target, DMI_SBADDRESS0, legal_address); - - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBBUSYERROR)) { - sbcs = set_field(sbcs_orig, DMI_SBCS_SBBUSYERROR, 1); - dmi_write(target, DMI_SBCS, sbcs); - dmi_read(target, &rd_val, DMI_SBCS); - if (get_field(rd_val, DMI_SBCS_SBBUSYERROR) == 0) + dmi_write(target, DM_SBADDRESS0, legal_address); + + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBBUSYERROR)) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBBUSYERROR, 1); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBBUSYERROR) == 0) LOG_INFO("System Bus Access Test 7: SBCS sbbusyerror test PASSED."); else { LOG_ERROR("System Bus Access Test 7: SBCS sbbusyerror test FAILED, unable to clear to 0."); @@ -3336,26 +4245,26 @@ void write_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t rd_sbcs; uint32_t masked_addr; - uint32_t sba_size = get_field(info->sbcs, DMI_SBCS_SBASIZE); + uint32_t sba_size = get_field(info->sbcs, DM_SBCS_SBASIZE); read_sbcs_nonbusy(target, &rd_sbcs); - uint32_t sbcs_no_readonaddr = set_field(sbcs, DMI_SBCS_SBREADONADDR, 0); - dmi_write(target, DMI_SBCS, sbcs_no_readonaddr); + uint32_t sbcs_no_readonaddr = set_field(sbcs, DM_SBCS_SBREADONADDR, 0); + dmi_write(target, DM_SBCS, sbcs_no_readonaddr); for (uint32_t i = 0; i < sba_size/32; i++) { masked_addr = (addr >> 32*i) & 0xffffffff; if (i != 3) - dmi_write(target, DMI_SBADDRESS0+i, masked_addr); + dmi_write(target, DM_SBADDRESS0+i, masked_addr); else - dmi_write(target, DMI_SBADDRESS3, masked_addr); + dmi_write(target, DM_SBADDRESS3, masked_addr); } /* Write SBDATA registers starting with highest address, since write to * SBDATA0 triggers write */ for (int i = write_size-1; i >= 0; i--) - dmi_write(target, DMI_SBDATA0+i, write_data[i]); + dmi_write(target, DM_SBDATA0+i, write_data[i]); } void read_memory_sba_simple(struct target *target, target_addr_t addr, @@ -3366,27 +4275,27 @@ void read_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t rd_sbcs; uint32_t masked_addr; - uint32_t sba_size = get_field(info->sbcs, DMI_SBCS_SBASIZE); + uint32_t sba_size = get_field(info->sbcs, DM_SBCS_SBASIZE); read_sbcs_nonbusy(target, &rd_sbcs); - uint32_t sbcs_readonaddr = set_field(sbcs, DMI_SBCS_SBREADONADDR, 1); - dmi_write(target, DMI_SBCS, sbcs_readonaddr); + uint32_t sbcs_readonaddr = set_field(sbcs, DM_SBCS_SBREADONADDR, 1); + dmi_write(target, DM_SBCS, sbcs_readonaddr); /* Write addresses starting with highest address register */ for (int i = sba_size/32-1; i >= 0; i--) { masked_addr = (addr >> 32*i) & 0xffffffff; if (i != 3) - dmi_write(target, DMI_SBADDRESS0+i, masked_addr); + dmi_write(target, DM_SBADDRESS0+i, masked_addr); else - dmi_write(target, DMI_SBADDRESS3, masked_addr); + dmi_write(target, DM_SBADDRESS3, masked_addr); } read_sbcs_nonbusy(target, &rd_sbcs); for (uint32_t i = 0; i < read_size; i++) - dmi_read(target, &(rd_buf[i]), DMI_SBDATA0+i); + dmi_read(target, &(rd_buf[i]), DM_SBDATA0+i); } int riscv013_dmi_write_u64_bits(struct target *target) @@ -3397,9 +4306,7 @@ int riscv013_dmi_write_u64_bits(struct target *target) static int maybe_execute_fence_i(struct target *target) { - RISCV013_INFO(info); - RISCV_INFO(r); - if (info->progbufsize + r->impebreak >= 3) + if (has_sufficient_progbuf(target, 3)) return execute_fence(target); return ERROR_OK; } @@ -3416,13 +4323,14 @@ static int riscv013_on_step_or_resume(struct target *target, bool step) if (result != ERROR_OK) return result; dcsr = set_field(dcsr, CSR_DCSR_STEP, step); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1); - dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1); + 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); } -static int riscv013_step_or_resume_current_hart(struct target *target, bool step) +static int riscv013_step_or_resume_current_hart(struct target *target, + bool step, bool use_hasel) { RISCV_INFO(r); LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step); @@ -3431,39 +4339,40 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step return ERROR_FAIL; } - if (maybe_execute_fence_i(target) != ERROR_OK) - return ERROR_FAIL; - /* Issue the resume command, and then wait for the current hart to resume. */ - uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE; + 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, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ); + dmi_write(target, DM_DMCONTROL, dmcontrol); + + dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HASEL, 0); + dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0); uint32_t dmstatus; for (size_t i = 0; i < 256; ++i) { usleep(10); if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; - if (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0) + if (get_field(dmstatus, DM_DMSTATUS_ALLRESUMEACK) == 0) continue; - if (step && get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0) + if (step && get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0) continue; - dmi_write(target, DMI_DMCONTROL, dmcontrol); + dmi_write(target, DM_DMCONTROL, dmcontrol); return ERROR_OK; } + dmi_write(target, DM_DMCONTROL, dmcontrol); + LOG_ERROR("unable to resume hart %d", r->current_hartid); - if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK) - return ERROR_FAIL; - LOG_ERROR(" dmcontrol=0x%08x", dmcontrol); if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; LOG_ERROR(" dmstatus =0x%08x", dmstatus); if (step) { LOG_ERROR(" was stepping, halting"); - riscv013_halt_current_hart(target); + riscv_halt(target); return ERROR_OK; } @@ -3475,9 +4384,9 @@ void 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, DMI_ABSTRACTCS); - while (get_field(abstractcs, DMI_ABSTRACTCS_BUSY)) { - dmi_read(target, &abstractcs, DMI_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 " @@ -3489,17 +4398,25 @@ void riscv013_clear_abstract_error(struct target *target) } } /* Clear the error status. */ - dmi_write(target, DMI_ABSTRACTCS, abstractcs & DMI_ABSTRACTCS_CMDERR); + dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); } +#ifdef _WIN32 +#define FILE_SEP '\\' +#else +#define FILE_SEP '/' +#endif #define COMPLIANCE_TEST(b, message) \ -{ \ +{ \ + const char *last_sep = strrchr(__FILE__, FILE_SEP); \ + const char *fname = (last_sep == NULL ? __FILE__ : last_sep + 1); \ + LOG_INFO("Executing test %d (%s:%d): %s", total_tests, fname, __LINE__, message); \ int pass = 0; \ if (b) { \ pass = 1; \ passed_tests++; \ } \ - LOG_INFO("%s test %d (%s)\n", (pass) ? "PASSED" : "FAILED", total_tests, message); \ + LOG_INFO(" %s", (pass) ? "PASSED" : "FAILED"); \ assert(pass); \ total_tests++; \ } @@ -3521,17 +4438,27 @@ void riscv013_clear_abstract_error(struct target *target) int riscv013_test_compliance(struct target *target) { - LOG_INFO("Testing Compliance against RISC-V Debug Spec v0.13"); + LOG_INFO("Basic compliance test against RISC-V Debug Spec v0.13"); + LOG_INFO("This test is not complete, and not well supported."); + LOG_INFO("Your core might pass this test without being compliant."); + LOG_INFO("Your core might fail this test while being compliant."); + LOG_INFO("Use your judgment, and please contribute improvements."); if (!riscv_rtos_enabled(target)) { LOG_ERROR("Please run with -rtos riscv to run compliance test."); return ERROR_FAIL; } + if (!target_was_examined(target)) { + LOG_ERROR("Cannot run compliance test, because target has not yet " + "been examined, or the examination failed.\n"); + return ERROR_FAIL; + } + int total_tests = 0; int passed_tests = 0; - uint32_t dmcontrol_orig = DMI_DMCONTROL_DMACTIVE; + uint32_t dmcontrol_orig = DM_DMCONTROL_DMACTIVE; uint32_t dmcontrol; uint32_t testvar; uint32_t testvar_read; @@ -3545,76 +4472,76 @@ int riscv013_test_compliance(struct target *target) or it is tied to 0. This check doesn't really do anything, but it does attempt to set the bit to 1 and then back to 0, which needs to work if its implemented. */ - COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 1)); - COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 0)); - COMPLIANCE_READ(target, &dmcontrol, DMI_DMCONTROL); - COMPLIANCE_TEST((get_field(dmcontrol, DMI_DMCONTROL_HARTRESET) == 0), + COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HARTRESET, 1)); + COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HARTRESET, 0)); + COMPLIANCE_READ(target, &dmcontrol, DM_DMCONTROL); + COMPLIANCE_TEST((get_field(dmcontrol, DM_DMCONTROL_HARTRESET) == 0), "DMCONTROL.hartreset can be 0 or RW."); /* hasel */ - COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 1)); - COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 0)); - COMPLIANCE_READ(target, &dmcontrol, DMI_DMCONTROL); - COMPLIANCE_TEST((get_field(dmcontrol, DMI_DMCONTROL_HASEL) == 0), + COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HASEL, 1)); + COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HASEL, 0)); + COMPLIANCE_READ(target, &dmcontrol, DM_DMCONTROL); + COMPLIANCE_TEST((get_field(dmcontrol, DM_DMCONTROL_HASEL) == 0), "DMCONTROL.hasel can be 0 or RW."); /* TODO: test that hamask registers exist if hasel does. */ /* haltreq */ - COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_halt(target)); /* This bit is not actually readable according to the spec, so nothing to check.*/ /* DMSTATUS */ - COMPLIANCE_CHECK_RO(target, DMI_DMSTATUS); + COMPLIANCE_CHECK_RO(target, DM_DMSTATUS); /* resumereq */ /* This bit is not actually readable according to the spec, so nothing to check.*/ - COMPLIANCE_MUST_PASS(riscv_resume_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_resume(target, true, 0, false, false, false)); /* Halt all harts again so the test can continue.*/ - COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_halt(target)); /* HARTINFO: Read-Only. This is per-hart, so need to adjust hartsel. */ uint32_t hartinfo; - COMPLIANCE_READ(target, &hartinfo, DMI_HARTINFO); + COMPLIANCE_READ(target, &hartinfo, DM_HARTINFO); for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) { COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel)); - COMPLIANCE_CHECK_RO(target, DMI_HARTINFO); + COMPLIANCE_CHECK_RO(target, DM_HARTINFO); /* $dscratch CSRs */ - uint32_t nscratch = get_field(hartinfo, DMI_HARTINFO_NSCRATCH); + uint32_t nscratch = get_field(hartinfo, DM_HARTINFO_NSCRATCH); for (unsigned int d = 0; d < nscratch; d++) { riscv_reg_t testval, testval_read; - /* Because DSCRATCH is not guaranteed to last across PB executions, need to put + /* Because DSCRATCH0 is not guaranteed to last across PB executions, need to put this all into one PB execution. Which may not be possible on all implementations.*/ if (info->progbufsize >= 5) { for (testval = 0x0011223300112233; testval != 0xDEAD; testval = testval == 0x0011223300112233 ? ~testval : 0xDEAD) { COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_S0, testval) == ERROR_OK, - "Need to be able to write S0 in order to test DSCRATCH."); + "Need to be able to write S0 in order to test DSCRATCH0."); struct riscv_program program32; riscv_program_init(&program32, target); - riscv_program_csrw(&program32, GDB_REGNO_S0, GDB_REGNO_DSCRATCH + d); - riscv_program_csrr(&program32, GDB_REGNO_S1, GDB_REGNO_DSCRATCH + d); + riscv_program_csrw(&program32, GDB_REGNO_S0, GDB_REGNO_DSCRATCH0 + d); + riscv_program_csrr(&program32, GDB_REGNO_S1, GDB_REGNO_DSCRATCH0 + d); riscv_program_fence(&program32); riscv_program_ebreak(&program32); COMPLIANCE_TEST(riscv_program_exec(&program32, target) == ERROR_OK, - "Accessing DSCRATCH with program buffer should succeed."); + "Accessing DSCRATCH0 with program buffer should succeed."); COMPLIANCE_TEST(register_read_direct(target, &testval_read, GDB_REGNO_S1) == ERROR_OK, - "Need to be able to read S1 in order to test DSCRATCH."); + "Need to be able to read S1 in order to test DSCRATCH0."); if (riscv_xlen(target) > 32) { COMPLIANCE_TEST(testval == testval_read, - "All DSCRATCH registers in HARTINFO must be R/W."); + "All DSCRATCH0 registers in HARTINFO must be R/W."); } else { COMPLIANCE_TEST(testval_read == (testval & 0xFFFFFFFF), - "All DSCRATCH registers in HARTINFO must be R/W."); + "All DSCRATCH0 registers in HARTINFO must be R/W."); } } } } /* TODO: dataaccess */ - if (get_field(hartinfo, DMI_HARTINFO_DATAACCESS)) { + if (get_field(hartinfo, DM_HARTINFO_DATAACCESS)) { /* TODO: Shadowed in memory map. */ /* TODO: datasize */ /* TODO: dataaddr */ @@ -3633,16 +4560,16 @@ int riscv013_test_compliance(struct target *target) for (int i = 0; i < MIN(riscv_count_harts(target), 32); i++) expected_haltsum0 |= (1 << i); - COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0); + COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM0); COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should report summary of up to 32 halted harts"); - COMPLIANCE_WRITE(target, DMI_HALTSUM0, 0xffffffff); - COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0); + COMPLIANCE_WRITE(target, DM_HALTSUM0, 0xffffffff); + COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM0); COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O"); - COMPLIANCE_WRITE(target, DMI_HALTSUM0, 0x0); - COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0); + COMPLIANCE_WRITE(target, DM_HALTSUM0, 0x0); + COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM0); COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O"); /* HALTSUM1 */ @@ -3650,16 +4577,16 @@ int riscv013_test_compliance(struct target *target) for (int i = 0; i < MIN(riscv_count_harts(target), 1024); i += 32) expected_haltsum1 |= (1 << (i/32)); - COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1); + COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1); COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should report summary of up to 1024 halted harts"); - COMPLIANCE_WRITE(target, DMI_HALTSUM1, 0xffffffff); - COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1); + COMPLIANCE_WRITE(target, DM_HALTSUM1, 0xffffffff); + COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1); COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O"); - COMPLIANCE_WRITE(target, DMI_HALTSUM1, 0x0); - COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1); + COMPLIANCE_WRITE(target, DM_HALTSUM1, 0x0); + COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1); COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O"); /* TODO: HAWINDOWSEL */ @@ -3669,38 +4596,38 @@ int riscv013_test_compliance(struct target *target) /* ABSTRACTCS */ uint32_t abstractcs; - COMPLIANCE_READ(target, &abstractcs, DMI_ABSTRACTCS); + COMPLIANCE_READ(target, &abstractcs, DM_ABSTRACTCS); /* Check that all reported Data Words are really R/W */ for (int invert = 0; invert < 2; invert++) { - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) { testvar = (i + 1) * 0x11111111; if (invert) testvar = ~testvar; - COMPLIANCE_WRITE(target, DMI_DATA0 + i, testvar); + COMPLIANCE_WRITE(target, DM_DATA0 + i, testvar); } - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) { testvar = (i + 1) * 0x11111111; if (invert) testvar = ~testvar; - COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i); + COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i); COMPLIANCE_TEST(testvar_read == testvar, "All reported DATA words must be R/W"); } } /* Check that all reported ProgBuf words are really R/W */ for (int invert = 0; invert < 2; invert++) { - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) { testvar = (i + 1) * 0x11111111; if (invert) testvar = ~testvar; - COMPLIANCE_WRITE(target, DMI_PROGBUF0 + i, testvar); + COMPLIANCE_WRITE(target, DM_PROGBUF0 + i, testvar); } - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) { testvar = (i + 1) * 0x11111111; if (invert) testvar = ~testvar; - COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i); + COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i); COMPLIANCE_TEST(testvar_read == testvar, "All reported PROGBUF words must be R/W"); } } @@ -3710,17 +4637,17 @@ int riscv013_test_compliance(struct target *target) /* COMMAND According to the spec, this register is only W, so can't really check the read result. But at any rate, this is not legal and should cause an error. */ - COMPLIANCE_WRITE(target, DMI_COMMAND, 0xAAAAAAAA); - COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); - COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, + COMPLIANCE_WRITE(target, DM_COMMAND, 0xAAAAAAAA); + COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS); + COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, "Illegal COMMAND should result in UNSUPPORTED"); - COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); + COMPLIANCE_WRITE(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); - COMPLIANCE_WRITE(target, DMI_COMMAND, 0x55555555); - COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); - COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, + COMPLIANCE_WRITE(target, DM_COMMAND, 0x55555555); + COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS); + COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, "Illegal COMMAND should result in UNSUPPORTED"); - COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); + COMPLIANCE_WRITE(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); /* Basic Abstract Commands */ for (unsigned int i = 1; i < 32; i = i << 1) { @@ -3742,11 +4669,11 @@ int riscv013_test_compliance(struct target *target) /* ABSTRACTAUTO See which bits are actually writable */ - COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF); + COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF); uint32_t abstractauto; uint32_t busy; - COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO); - COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0x0); + COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO); + COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0x0); if (abstractauto > 0) { /* This mechanism only works when you have a reasonable sized progbuf, which is not a true compliance requirement. */ @@ -3761,39 +4688,39 @@ int riscv013_test_compliance(struct target *target) COMPLIANCE_MUST_PASS(riscv_program_insert(&program, wfi())); COMPLIANCE_MUST_PASS(riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, 1)); COMPLIANCE_MUST_PASS(riscv_program_ebreak(&program)); - COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0x0); + COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0x0); COMPLIANCE_MUST_PASS(riscv_program_exec(&program, target)); testvar++; - COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF); - COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO); - uint32_t autoexec_data = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECDATA); - uint32_t autoexec_progbuf = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF); + COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF); + COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO); + uint32_t autoexec_data = get_field(abstractauto, DM_ABSTRACTAUTO_AUTOEXECDATA); + uint32_t autoexec_progbuf = get_field(abstractauto, DM_ABSTRACTAUTO_AUTOEXECPROGBUF); for (unsigned int i = 0; i < 12; i++) { - COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i); + COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i); do { - COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); - busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY); + COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS); + busy = get_field(testvar_read, DM_ABSTRACTCS_BUSY); } while (busy); if (autoexec_data & (1 << i)) { - COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT), + COMPLIANCE_TEST(i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT), "AUTOEXEC may be writable up to DATACOUNT bits."); testvar++; } } for (unsigned int i = 0; i < 16; i++) { - COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i); + COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i); do { - COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); - busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY); + COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS); + busy = get_field(testvar_read, DM_ABSTRACTCS_BUSY); } while (busy); if (autoexec_progbuf & (1 << i)) { - COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE), + COMPLIANCE_TEST(i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE), "AUTOEXEC may be writable up to PROGBUFSIZE bits."); testvar++; } } - COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0); + COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0); COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &value, GDB_REGNO_S0), "Need to be able to read S0 to test ABSTRACTAUTO"); @@ -3852,20 +4779,20 @@ int riscv013_test_compliance(struct target *target) */ /* Write some registers. They should not be impacted by ndmreset. */ - COMPLIANCE_WRITE(target, DMI_COMMAND, 0xFFFFFFFF); + COMPLIANCE_WRITE(target, DM_COMMAND, 0xFFFFFFFF); - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) { testvar = (i + 1) * 0x11111111; - COMPLIANCE_WRITE(target, DMI_PROGBUF0 + i, testvar); + COMPLIANCE_WRITE(target, DM_PROGBUF0 + i, testvar); } - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) { testvar = (i + 1) * 0x11111111; - COMPLIANCE_WRITE(target, DMI_DATA0 + i, testvar); + COMPLIANCE_WRITE(target, DM_DATA0 + i, testvar); } - COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF); - COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO); + COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF); + COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO); /* Pulse reset. */ target->reset_halt = true; @@ -3874,25 +4801,25 @@ int riscv013_test_compliance(struct target *target) COMPLIANCE_TEST(ERROR_OK == deassert_reset(target), "Must be able to deassert NDMRESET"); /* Verify that most stuff is not affected by ndmreset. */ - COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); - COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, - "NDMRESET should not affect DMI_ABSTRACTCS"); - COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTAUTO); - COMPLIANCE_TEST(testvar_read == abstractauto, "NDMRESET should not affect DMI_ABSTRACTAUTO"); + COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS); + COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, + "NDMRESET should not affect DM_ABSTRACTCS"); + COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTAUTO); + COMPLIANCE_TEST(testvar_read == abstractauto, "NDMRESET should not affect DM_ABSTRACTAUTO"); /* Clean up to avoid future test failures */ - COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); - COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0); + COMPLIANCE_WRITE(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); + COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0); - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) { testvar = (i + 1) * 0x11111111; - COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i); + COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i); COMPLIANCE_TEST(testvar_read == testvar, "PROGBUF words must not be affected by NDMRESET"); } - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) { testvar = (i + 1) * 0x11111111; - COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i); + COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i); COMPLIANCE_TEST(testvar_read == testvar, "DATA words must not be affected by NDMRESET"); } @@ -3909,20 +4836,20 @@ int riscv013_test_compliance(struct target *target) /* DMACTIVE -- deasserting DMACTIVE should reset all the above values. */ /* Toggle dmactive */ - COMPLIANCE_WRITE(target, DMI_DMCONTROL, 0); - COMPLIANCE_WRITE(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE); - COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); - COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == 0, "ABSTRACTCS.cmderr should reset to 0"); - COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTAUTO); + COMPLIANCE_WRITE(target, DM_DMCONTROL, 0); + COMPLIANCE_WRITE(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE); + COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS); + COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR) == 0, "ABSTRACTCS.cmderr should reset to 0"); + COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTAUTO); COMPLIANCE_TEST(testvar_read == 0, "ABSTRACTAUTO should reset to 0"); - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { - COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i); + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) { + COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i); COMPLIANCE_TEST(testvar_read == 0, "PROGBUF words should reset to 0"); } - for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { - COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i); + for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) { + COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i); COMPLIANCE_TEST(testvar_read == 0, "DATA words should reset to 0"); } @@ -3936,7 +4863,7 @@ int riscv013_test_compliance(struct target *target) */ /* Halt every hart for any follow-up tests*/ - COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + COMPLIANCE_MUST_PASS(riscv_halt(target)); uint32_t failed_tests = total_tests - passed_tests; if (total_tests == passed_tests) { diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index de2f095be2..4ef969b016 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + #include #include #include @@ -18,48 +20,6 @@ #include "gdb_regs.h" #include "rtos/rtos.h" -/** - * Since almost everything can be accomplish by scanning the dbus register, all - * functions here assume dbus is already selected. The exception are functions - * called directly by OpenOCD, which can't assume anything about what's - * currently in IR. They should set IR to dbus explicitly. - */ - -/** - * Code structure - * - * At the bottom of the stack are the OpenOCD JTAG functions: - * jtag_add_[id]r_scan - * jtag_execute_query - * jtag_add_runtest - * - * There are a few functions to just instantly shift a register and get its - * value: - * dtmcontrol_scan - * idcode_scan - * dbus_scan - * - * Because doing one scan and waiting for the result is slow, most functions - * batch up a bunch of dbus writes and then execute them all at once. They use - * the scans "class" for this: - * scans_new - * scans_delete - * scans_execute - * scans_add_... - * Usually you new(), call a bunch of add functions, then execute() and look - * at the results by calling scans_get...() - * - * Optimized functions will directly use the scans class above, but slightly - * lazier code will use the cache functions that in turn use the scans - * functions: - * cache_get... - * cache_set... - * cache_write - * cache_set... update a local structure, which is then synced to the target - * with cache_write(). Only Debug RAM words that are actually changed are sent - * to the target. Afterwards use cache_get... to read results. - */ - #define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) #define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) @@ -108,12 +68,6 @@ typedef enum { #define DBUS_DATA_SIZE 34 #define DBUS_ADDRESS_START 36 -typedef enum { - RE_OK, - RE_FAIL, - RE_AGAIN -} riscv_error_t; - typedef enum slot { SLOT0, SLOT1, @@ -170,6 +124,71 @@ struct scan_field select_idcode = { .out_value = ir_idcode }; +bscan_tunnel_type_t bscan_tunnel_type; +int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */ + +static uint8_t bscan_zero[4] = {0}; +static uint8_t bscan_one[4] = {1}; + +uint8_t ir_user4[4] = {0x23}; +struct scan_field select_user4 = { + .in_value = NULL, + .out_value = ir_user4 +}; + + +uint8_t bscan_tunneled_ir_width[4] = {5}; /* overridden by assignment in riscv_init_target */ +struct scan_field _bscan_tunnel_data_register_select_dmi[] = { + { + .num_bits = 3, + .out_value = bscan_zero, + .in_value = NULL, + }, + { + .num_bits = 5, /* initialized in riscv_init_target to ir width of DM */ + .out_value = ir_dbus, + .in_value = NULL, + }, + { + .num_bits = 7, + .out_value = bscan_tunneled_ir_width, + .in_value = NULL, + }, + { + .num_bits = 1, + .out_value = bscan_zero, + .in_value = NULL, + } +}; + +struct scan_field _bscan_tunnel_nested_tap_select_dmi[] = { + { + .num_bits = 1, + .out_value = bscan_zero, + .in_value = NULL, + }, + { + .num_bits = 7, + .out_value = bscan_tunneled_ir_width, + .in_value = NULL, + }, + { + .num_bits = 0, /* initialized in riscv_init_target to ir width of DM */ + .out_value = ir_dbus, + .in_value = NULL, + }, + { + .num_bits = 3, + .out_value = bscan_zero, + .in_value = NULL, + } +}; +struct scan_field *bscan_tunnel_nested_tap_select_dmi = _bscan_tunnel_nested_tap_select_dmi; +uint32_t bscan_tunnel_nested_tap_select_dmi_num_fields = DIM(_bscan_tunnel_nested_tap_select_dmi); + +struct scan_field *bscan_tunnel_data_register_select_dmi = _bscan_tunnel_data_register_select_dmi; +uint32_t bscan_tunnel_data_register_select_dmi_num_fields = DIM(_bscan_tunnel_data_register_select_dmi); + struct trigger { uint64_t address; uint32_t length; @@ -186,6 +205,12 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC; int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC; bool riscv_prefer_sba; +bool riscv_enable_virt2phys = true; +bool riscv_ebreakm = true; +bool riscv_ebreaks = true; +bool riscv_ebreaku = true; + +bool riscv_enable_virtual; typedef struct { uint16_t low, high; @@ -199,12 +224,159 @@ range_t *expose_csr; /* Same, but for custom registers. */ range_t *expose_custom; +static enum { + RO_NORMAL, + RO_REVERSED +} resume_order; + +virt2phys_info_t sv32 = { + .name = "Sv32", + .va_bits = 32, + .level = 2, + .pte_shift = 2, + .vpn_shift = {12, 22}, + .vpn_mask = {0x3ff, 0x3ff}, + .pte_ppn_shift = {10, 20}, + .pte_ppn_mask = {0x3ff, 0xfff}, + .pa_ppn_shift = {12, 22}, + .pa_ppn_mask = {0x3ff, 0xfff}, +}; + +virt2phys_info_t sv39 = { + .name = "Sv39", + .va_bits = 39, + .level = 3, + .pte_shift = 3, + .vpn_shift = {12, 21, 30}, + .vpn_mask = {0x1ff, 0x1ff, 0x1ff}, + .pte_ppn_shift = {10, 19, 28}, + .pte_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff}, + .pa_ppn_shift = {12, 21, 30}, + .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff}, +}; + +virt2phys_info_t sv48 = { + .name = "Sv48", + .va_bits = 48, + .level = 4, + .pte_shift = 3, + .vpn_shift = {12, 21, 30, 39}, + .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff}, + .pte_ppn_shift = {10, 19, 28, 37}, + .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff}, + .pa_ppn_shift = {12, 21, 30, 39}, + .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff}, +}; + +static int riscv_resume_go_all_harts(struct target *target); + +void select_dmi_via_bscan(struct target *target) +{ + jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE); + if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) + jtag_add_dr_scan(target->tap, bscan_tunnel_data_register_select_dmi_num_fields, + bscan_tunnel_data_register_select_dmi, TAP_IDLE); + else /* BSCAN_TUNNEL_NESTED_TAP */ + jtag_add_dr_scan(target->tap, bscan_tunnel_nested_tap_select_dmi_num_fields, + bscan_tunnel_nested_tap_select_dmi, TAP_IDLE); +} + +uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) +{ + /* On BSCAN TAP: Select IR=USER4, issue tunneled IR scan via BSCAN TAP's DR */ + uint8_t tunneled_ir_width[4] = {bscan_tunnel_ir_width}; + uint8_t tunneled_dr_width[4] = {32}; + uint8_t out_value[5] = {0}; + uint8_t in_value[5] = {0}; + + buf_set_u32(out_value, 0, 32, out); + struct scan_field tunneled_ir[4] = {}; + struct scan_field tunneled_dr[4] = {}; + + if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) { + tunneled_ir[0].num_bits = 3; + tunneled_ir[0].out_value = bscan_zero; + tunneled_ir[0].in_value = NULL; + tunneled_ir[1].num_bits = bscan_tunnel_ir_width; + tunneled_ir[1].out_value = ir_dtmcontrol; + tunneled_ir[1].in_value = NULL; + tunneled_ir[2].num_bits = 7; + tunneled_ir[2].out_value = tunneled_ir_width; + tunneled_ir[2].in_value = NULL; + tunneled_ir[3].num_bits = 1; + tunneled_ir[3].out_value = bscan_zero; + tunneled_ir[3].in_value = NULL; + + tunneled_dr[0].num_bits = 3; + tunneled_dr[0].out_value = bscan_zero; + tunneled_dr[0].in_value = NULL; + tunneled_dr[1].num_bits = 32 + 1; + tunneled_dr[1].out_value = out_value; + tunneled_dr[1].in_value = in_value; + tunneled_dr[2].num_bits = 7; + tunneled_dr[2].out_value = tunneled_dr_width; + tunneled_dr[2].in_value = NULL; + tunneled_dr[3].num_bits = 1; + tunneled_dr[3].out_value = bscan_one; + tunneled_dr[3].in_value = NULL; + } else { + /* BSCAN_TUNNEL_NESTED_TAP */ + tunneled_ir[3].num_bits = 3; + tunneled_ir[3].out_value = bscan_zero; + tunneled_ir[3].in_value = NULL; + tunneled_ir[2].num_bits = bscan_tunnel_ir_width; + tunneled_ir[2].out_value = ir_dtmcontrol; + tunneled_ir[1].in_value = NULL; + tunneled_ir[1].num_bits = 7; + tunneled_ir[1].out_value = tunneled_ir_width; + tunneled_ir[2].in_value = NULL; + tunneled_ir[0].num_bits = 1; + tunneled_ir[0].out_value = bscan_zero; + tunneled_ir[0].in_value = NULL; + + tunneled_dr[3].num_bits = 3; + tunneled_dr[3].out_value = bscan_zero; + tunneled_dr[3].in_value = NULL; + tunneled_dr[2].num_bits = 32 + 1; + tunneled_dr[2].out_value = out_value; + tunneled_dr[2].in_value = in_value; + tunneled_dr[1].num_bits = 7; + tunneled_dr[1].out_value = tunneled_dr_width; + tunneled_dr[1].in_value = NULL; + tunneled_dr[0].num_bits = 1; + tunneled_dr[0].out_value = bscan_one; + tunneled_dr[0].in_value = NULL; + } + jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE); + jtag_add_dr_scan(target->tap, DIM(tunneled_ir), tunneled_ir, TAP_IDLE); + jtag_add_dr_scan(target->tap, DIM(tunneled_dr), tunneled_dr, TAP_IDLE); + select_dmi_via_bscan(target); + + int retval = jtag_execute_queue(); + if (retval != ERROR_OK) { + LOG_ERROR("failed jtag scan: %d", retval); + return retval; + } + /* Note the starting offset is bit 1, not bit 0. In BSCAN tunnel, there is a one-bit TCK skew between + output and input */ + uint32_t in = buf_get_u32(in_value, 1, 32); + LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in); + + return in; +} + + + static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) { struct scan_field field; uint8_t in_value[4]; uint8_t out_value[4] = { 0 }; + if (bscan_tunnel_ir_width != 0) + return dtmcontrol_scan_via_bscan(target, out); + + buf_set_u32(out_value, 0, 32, out); jtag_add_ir_scan(target->tap, &select_dtmcontrol, TAP_IDLE); @@ -264,6 +436,15 @@ static int riscv_init_target(struct command_context *cmd_ctx, select_dbus.num_bits = target->tap->ir_length; select_idcode.num_bits = target->tap->ir_length; + if (bscan_tunnel_ir_width != 0) { + select_user4.num_bits = target->tap->ir_length; + bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width; + if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) + bscan_tunnel_data_register_select_dmi[1].num_bits = bscan_tunnel_ir_width; + else /* BSCAN_TUNNEL_NESTED_TAP */ + bscan_tunnel_nested_tap_select_dmi[2].num_bits = bscan_tunnel_ir_width; + } + riscv_semihosting_init(target); target->debug_reason = DBG_REASON_DBGRQ; @@ -302,12 +483,6 @@ static void riscv_deinit_target(struct target *target) target->arch_info = NULL; } -static int oldriscv_halt(struct target *target) -{ - struct target_type *tt = get_target_type(target); - return tt->halt(target); -} - static void trigger_from_breakpoint(struct trigger *trigger, const struct breakpoint *breakpoint) { @@ -691,6 +866,8 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi { struct watchpoint *wp = target->watchpoints; + if (riscv_rtos_enabled(target)) + riscv_set_current_hartid(target, target->rtos->current_thread - 1); LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target)); /*TODO instead of disassembling the instruction that we think caused the @@ -825,13 +1002,126 @@ static int old_or_new_riscv_poll(struct target *target) return riscv_openocd_poll(target); } -static int old_or_new_riscv_halt(struct target *target) +int halt_prep(struct target *target) { RISCV_INFO(r); - if (r->is_halted == NULL) - return oldriscv_halt(target); - else - return riscv_openocd_halt(target); + for (int i = 0; i < riscv_count_harts(target); ++i) { + if (!riscv_hart_enabled(target, i)) + continue; + + LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target), + target->debug_reason); + if (riscv_set_current_hartid(target, i) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + LOG_DEBUG("Hart %d is already halted (reason=%d).", i, + target->debug_reason); + } else { + if (r->halt_prep(target) != ERROR_OK) + return ERROR_FAIL; + r->prepped = true; + } + } + return ERROR_OK; +} + +int riscv_halt_go_all_harts(struct target *target) +{ + RISCV_INFO(r); + for (int i = 0; i < riscv_count_harts(target); ++i) { + if (!riscv_hart_enabled(target, i)) + continue; + + if (riscv_set_current_hartid(target, i) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + LOG_DEBUG("Hart %d is already halted.", i); + } else { + if (r->halt_go(target) != ERROR_OK) + return ERROR_FAIL; + } + } + + riscv_invalidate_register_cache(target); + + return ERROR_OK; +} + +int halt_go(struct target *target) +{ + riscv_info_t *r = riscv_info(target); + int result; + if (r->is_halted == NULL) { + struct target_type *tt = get_target_type(target); + result = tt->halt(target); + } else { + result = riscv_halt_go_all_harts(target); + } + target->state = TARGET_HALTED; + if (target->debug_reason == DBG_REASON_NOTHALTED) + target->debug_reason = DBG_REASON_DBGRQ; + + return result; +} + +static int halt_finish(struct target *target) +{ + return target_call_event_callbacks(target, TARGET_EVENT_HALTED); +} + +int riscv_halt(struct target *target) +{ + RISCV_INFO(r); + + if (r->is_halted == NULL) { + struct target_type *tt = get_target_type(target); + return tt->halt(target); + } + + LOG_DEBUG("[%d] halting all harts", target->coreid); + + int result = ERROR_OK; + if (target->smp) { + for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target *t = tlist->target; + if (halt_prep(t) != ERROR_OK) + result = ERROR_FAIL; + } + + for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target *t = tlist->target; + riscv_info_t *i = riscv_info(t); + if (i->prepped) { + if (halt_go(t) != ERROR_OK) + result = ERROR_FAIL; + } + } + + for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target *t = tlist->target; + if (halt_finish(t) != ERROR_OK) + return ERROR_FAIL; + } + + } else { + if (halt_prep(target) != ERROR_OK) + result = ERROR_FAIL; + if (halt_go(target) != ERROR_OK) + result = ERROR_FAIL; + if (halt_finish(target) != ERROR_OK) + return ERROR_FAIL; + } + + if (riscv_rtos_enabled(target)) { + if (r->rtos_hartid != -1) { + LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid); + target->rtos->current_threadid = r->rtos_hartid + 1; + target->rtos->current_thread = r->rtos_hartid + 1; + } else + LOG_DEBUG("halt requested, but no known RTOS hartid"); + } + + return result; } static int riscv_assert_reset(struct target *target) @@ -849,44 +1139,243 @@ static int riscv_deassert_reset(struct target *target) return tt->deassert_reset(target); } +int riscv_resume_prep_all_harts(struct target *target) +{ + RISCV_INFO(r); + for (int i = 0; i < riscv_count_harts(target); ++i) { + if (!riscv_hart_enabled(target, i)) + continue; -static int oldriscv_resume(struct target *target, int current, uint32_t address, - int handle_breakpoints, int debug_execution) + LOG_DEBUG("prep hart %d", i); + if (riscv_set_current_hartid(target, i) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + if (r->resume_prep(target) != ERROR_OK) + return ERROR_FAIL; + } else { + LOG_DEBUG(" hart %d requested resume, but was already resumed", i); + } + } + + LOG_DEBUG("[%d] mark as prepped", target->coreid); + 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) { - struct target_type *tt = get_target_type(target); - return tt->resume(target, current, address, handle_breakpoints, - debug_execution); + RISCV_INFO(r); + + LOG_DEBUG("deal with triggers"); + + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + + int hartid = riscv_current_hartid(target); + if (r->manual_hwbp_set) { + /* Look at every trigger that may have been set. */ + riscv_reg_t tselect; + if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; + for (unsigned t = 0; t < r->trigger_count[hartid]; t++) { + if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) + return ERROR_FAIL; + riscv_reg_t tdata1; + if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK) + return ERROR_FAIL; + if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) { + state[t] = tdata1; + if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK) + return ERROR_FAIL; + } + } + if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK) + return ERROR_FAIL; + + } else { + /* Just go through the triggers we manage. */ + struct watchpoint *watchpoint = target->watchpoints; + int i = 0; + while (watchpoint) { + LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set); + state[i] = watchpoint->set; + if (watchpoint->set) { + if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK) + return ERROR_FAIL; + } + watchpoint = watchpoint->next; + i++; + } + } + + return ERROR_OK; +} + +static int enable_triggers(struct target *target, riscv_reg_t *state) +{ + RISCV_INFO(r); + + int hartid = riscv_current_hartid(target); + + if (r->manual_hwbp_set) { + /* Look at every trigger that may have been set. */ + riscv_reg_t tselect; + if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; + for (unsigned t = 0; t < r->trigger_count[hartid]; t++) { + if (state[t] != 0) { + if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) + return ERROR_FAIL; + if (riscv_set_register(target, GDB_REGNO_TDATA1, state[t]) != ERROR_OK) + return ERROR_FAIL; + } + } + if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK) + return ERROR_FAIL; + + } else { + struct watchpoint *watchpoint = target->watchpoints; + int i = 0; + while (watchpoint) { + LOG_DEBUG("watchpoint %d: cleared=%" PRId64, i, state[i]); + if (state[i]) { + if (riscv_add_watchpoint(target, watchpoint) != ERROR_OK) + return ERROR_FAIL; + } + watchpoint = watchpoint->next; + i++; + } + } + + return ERROR_OK; +} + +/** + * Get everything ready to resume. + */ +static int resume_prep(struct target *target, int current, + target_addr_t address, int handle_breakpoints, int debug_execution) +{ + RISCV_INFO(r); + LOG_DEBUG("[%d]", target->coreid); + + if (!current) + riscv_set_register(target, GDB_REGNO_PC, address); + + if (target->debug_reason == DBG_REASON_WATCHPOINT) { + /* To be able to run off a trigger, disable all the triggers, step, and + * then resume as usual. */ + riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0}; + + if (disable_triggers(target, trigger_state) != ERROR_OK) + return ERROR_FAIL; + + if (old_or_new_riscv_step(target, true, 0, false) != ERROR_OK) + return ERROR_FAIL; + + if (enable_triggers(target, trigger_state) != ERROR_OK) + return ERROR_FAIL; + } + + if (r->is_halted) { + if (riscv_resume_prep_all_harts(target) != ERROR_OK) + return ERROR_FAIL; + } + + LOG_DEBUG("[%d] mark as prepped", target->coreid); + r->prepped = true; + + return ERROR_OK; } -static int old_or_new_riscv_resume(struct target *target, int current, +/** + * Resume all the harts that have been prepped, as close to instantaneous as + * possible. + */ +static int resume_go(struct target *target, int current, target_addr_t address, int handle_breakpoints, int debug_execution) +{ + riscv_info_t *r = riscv_info(target); + int result; + if (r->is_halted == NULL) { + struct target_type *tt = get_target_type(target); + result = tt->resume(target, current, address, handle_breakpoints, + debug_execution); + } else { + result = riscv_resume_go_all_harts(target); + } + + return result; +} + +static int resume_finish(struct target *target) +{ + register_cache_invalidate(target->reg_cache); + + target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; + return target_call_event_callbacks(target, TARGET_EVENT_RESUMED); +} + +/** + * @par single_hart When true, only resume a single hart even if SMP is + * configured. This is used to run algorithms on just one hart. + */ +int riscv_resume( + struct target *target, + int current, + target_addr_t address, + int handle_breakpoints, + int debug_execution, + bool single_hart) { LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints); - if (target->smp) { - struct target_list *targets = target->head; - int result = ERROR_OK; - while (targets) { - struct target *t = targets->target; - riscv_info_t *r = riscv_info(t); - if (r->is_halted == NULL) { - if (oldriscv_resume(t, current, address, handle_breakpoints, + int result = ERROR_OK; + if (target->smp && !single_hart) { + for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target *t = tlist->target; + if (resume_prep(t, current, address, handle_breakpoints, + debug_execution) != ERROR_OK) + result = ERROR_FAIL; + } + + for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target *t = tlist->target; + riscv_info_t *i = riscv_info(t); + if (i->prepped) { + if (resume_go(t, current, address, handle_breakpoints, debug_execution) != ERROR_OK) result = ERROR_FAIL; - } else { - if (riscv_openocd_resume(t, current, address, - handle_breakpoints, debug_execution) != ERROR_OK) - result = ERROR_FAIL; } - targets = targets->next; } - return result; + + for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target *t = tlist->target; + if (resume_finish(t) != ERROR_OK) + return ERROR_FAIL; + } + + } else { + if (resume_prep(target, current, address, handle_breakpoints, + debug_execution) != ERROR_OK) + result = ERROR_FAIL; + if (resume_go(target, current, address, handle_breakpoints, + debug_execution) != ERROR_OK) + result = ERROR_FAIL; + if (resume_finish(target) != ERROR_OK) + return ERROR_FAIL; } - RISCV_INFO(r); - if (r->is_halted == NULL) - return oldriscv_resume(target, current, address, handle_breakpoints, debug_execution); - else - return riscv_openocd_resume(target, current, address, handle_breakpoints, debug_execution); + return result; +} + +static int riscv_target_resume(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution) +{ + return riscv_resume(target, current, address, handle_breakpoints, + debug_execution, false); } static int riscv_select_current_hart(struct target *target) @@ -900,20 +1389,229 @@ static int riscv_select_current_hart(struct target *target) return riscv_set_current_hartid(target, target->coreid); } +static int riscv_mmu(struct target *target, int *enabled) +{ + if (!riscv_enable_virt2phys) { + *enabled = 0; + return ERROR_OK; + } + + if (riscv_rtos_enabled(target)) + riscv_set_current_hartid(target, target->rtos->current_thread - 1); + + /* Don't use MMU in explicit or effective M (machine) mode */ + riscv_reg_t priv; + if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) { + LOG_ERROR("Failed to read priv register."); + return ERROR_FAIL; + } + + riscv_reg_t mstatus; + if (riscv_get_register(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) { + LOG_ERROR("Failed to read mstatus register."); + return ERROR_FAIL; + } + + if ((get_field(mstatus, MSTATUS_MPRV) ? get_field(mstatus, MSTATUS_MPP) : priv) == PRV_M) { + LOG_DEBUG("SATP/MMU ignored in Machine mode (mstatus=0x%" PRIx64 ").", mstatus); + *enabled = 0; + return ERROR_OK; + } + + riscv_reg_t satp; + if (riscv_get_register(target, &satp, GDB_REGNO_SATP) != ERROR_OK) { + LOG_DEBUG("Couldn't read SATP."); + /* If we can't read SATP, then there must not be an MMU. */ + *enabled = 0; + return ERROR_OK; + } + + if (get_field(satp, RISCV_SATP_MODE(riscv_xlen(target))) == SATP_MODE_OFF) { + LOG_DEBUG("MMU is disabled."); + *enabled = 0; + } else { + LOG_DEBUG("MMU is enabled."); + *enabled = 1; + } + + return ERROR_OK; +} + +static int riscv_address_translate(struct target *target, + target_addr_t virtual, target_addr_t *physical) +{ + RISCV_INFO(r); + riscv_reg_t satp_value; + int mode; + uint64_t ppn_value; + target_addr_t table_address; + virt2phys_info_t *info; + uint64_t pte; + int i; + + if (riscv_rtos_enabled(target)) + riscv_set_current_hartid(target, target->rtos->current_thread - 1); + + int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP); + if (result != ERROR_OK) + return result; + + unsigned xlen = riscv_xlen(target); + mode = get_field(satp_value, RISCV_SATP_MODE(xlen)); + switch (mode) { + case SATP_MODE_SV32: + info = &sv32; + break; + case SATP_MODE_SV39: + info = &sv39; + break; + case SATP_MODE_SV48: + info = &sv48; + break; + case SATP_MODE_OFF: + LOG_ERROR("No translation or protection." \ + " (satp: 0x%" PRIx64 ")", satp_value); + return ERROR_FAIL; + default: + LOG_ERROR("The translation mode is not supported." \ + " (satp: 0x%" PRIx64 ")", satp_value); + return ERROR_FAIL; + } + LOG_DEBUG("virtual=0x%" TARGET_PRIxADDR "; mode=%s", virtual, info->name); + + /* verify bits xlen-1:va_bits-1 are all equal */ + target_addr_t mask = ((target_addr_t)1 << (xlen - (info->va_bits - 1))) - 1; + target_addr_t masked_msbs = (virtual >> (info->va_bits - 1)) & mask; + if (masked_msbs != 0 && masked_msbs != mask) { + LOG_ERROR("Virtual address 0x%" TARGET_PRIxADDR " is not sign-extended " + "for %s mode.", virtual, info->name); + return ERROR_FAIL; + } + + ppn_value = get_field(satp_value, RISCV_SATP_PPN(xlen)); + table_address = ppn_value << RISCV_PGSHIFT; + i = info->level - 1; + while (i >= 0) { + uint64_t vpn = virtual >> info->vpn_shift[i]; + vpn &= info->vpn_mask[i]; + target_addr_t pte_address = table_address + + (vpn << info->pte_shift); + uint8_t buffer[8]; + assert(info->pte_shift <= 3); + int retval = r->read_memory(target, pte_address, + 4, (1 << info->pte_shift) / 4, buffer, 4); + if (retval != ERROR_OK) + return ERROR_FAIL; + + if (info->pte_shift == 2) + pte = buf_get_u32(buffer, 0, 32); + else + pte = buf_get_u64(buffer, 0, 64); + + LOG_DEBUG("i=%d; PTE @0x%" TARGET_PRIxADDR " = 0x%" PRIx64, i, + pte_address, pte); + + if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) + return ERROR_FAIL; + + if ((pte & PTE_R) || (pte & PTE_X)) /* Found leaf PTE. */ + break; + + i--; + if (i < 0) + break; + ppn_value = pte >> PTE_PPN_SHIFT; + table_address = ppn_value << RISCV_PGSHIFT; + } + + if (i < 0) { + LOG_ERROR("Couldn't find the PTE."); + return ERROR_FAIL; + } + + /* Make sure to clear out the high bits that may be set. */ + *physical = virtual & (((target_addr_t)1 << info->va_bits) - 1); + + while (i < info->level) { + ppn_value = pte >> info->pte_ppn_shift[i]; + ppn_value &= info->pte_ppn_mask[i]; + *physical &= ~(((target_addr_t)info->pa_ppn_mask[i]) << + info->pa_ppn_shift[i]); + *physical |= (ppn_value << info->pa_ppn_shift[i]); + i++; + } + LOG_DEBUG("0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, virtual, + *physical); + + return ERROR_OK; +} + +static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_addr_t *physical) +{ + int enabled; + if (riscv_mmu(target, &enabled) == ERROR_OK) { + if (!enabled) + return ERROR_FAIL; + + if (riscv_address_translate(target, virtual, physical) == ERROR_OK) + return ERROR_OK; + } + + return ERROR_FAIL; +} + +static int riscv_read_phys_memory(struct target *target, target_addr_t phys_address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + RISCV_INFO(r); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + return r->read_memory(target, phys_address, size, count, buffer, size); +} + static int riscv_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer) +{ + if (count == 0) { + LOG_WARNING("0-length read from 0x%" TARGET_PRIxADDR, address); + return ERROR_OK; + } + + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + + target_addr_t physical_addr; + if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK) + address = physical_addr; + + RISCV_INFO(r); + return r->read_memory(target, address, size, count, buffer, size); +} + +static int riscv_write_phys_memory(struct target *target, target_addr_t phys_address, + uint32_t size, uint32_t count, const uint8_t *buffer) { if (riscv_select_current_hart(target) != ERROR_OK) return ERROR_FAIL; struct target_type *tt = get_target_type(target); - return tt->read_memory(target, address, size, count, buffer); + return tt->write_memory(target, phys_address, size, count, buffer); } static int riscv_write_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { + if (count == 0) { + LOG_WARNING("0-length write to 0x%" TARGET_PRIxADDR, address); + return ERROR_OK; + } + if (riscv_select_current_hart(target) != ERROR_OK) return ERROR_FAIL; + + target_addr_t physical_addr; + if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK) + address = physical_addr; + struct target_type *tt = get_target_type(target); return tt->write_memory(target, address, size, count, buffer); } @@ -954,20 +1652,26 @@ 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 && !target->reg_cache->reg_list[i].valid) { + if (read && + target->reg_cache->reg_list[i].exist && + !target->reg_cache->reg_list[i].valid) { if (target->reg_cache->reg_list[i].type->get( &target->reg_cache->reg_list[i]) != ERROR_OK) - /* This function is called when first connecting to gdb, - * resulting in an attempt to read all kinds of registers which - * probably will fail. Ignore these failures, and when - * encountered stop reading to save time. */ - read = false; + return ERROR_FAIL; } } return ERROR_OK; } +static int riscv_get_gdb_reg_list_noread(struct target *target, + struct reg **reg_list[], int *reg_list_size, + enum target_register_class reg_class) +{ + return riscv_get_gdb_reg_list_internal(target, reg_list, reg_list_size, + reg_class, false); +} + static int riscv_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size, enum target_register_class reg_class) @@ -989,6 +1693,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, target_addr_t exit_point, int timeout_ms, void *arch_info) { riscv_info_t *info = (riscv_info_t *) target->arch_info; + int hartid = riscv_current_hartid(target); if (num_mem_params > 0) { LOG_ERROR("Memory parameters are not supported for RISC-V algorithms."); @@ -1005,12 +1710,10 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK) return ERROR_FAIL; uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size); + LOG_DEBUG("saved_pc=0x%" PRIx64, saved_pc); uint64_t saved_regs[32]; for (int i = 0; i < num_reg_params; i++) { - if (reg_params[i].direction == PARAM_IN) - continue; - LOG_DEBUG("save %s", reg_params[i].reg_name); struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0); if (!r) { @@ -1032,8 +1735,11 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, if (r->type->get(r) != ERROR_OK) return ERROR_FAIL; saved_regs[r->number] = buf_get_u64(r->value, 0, r->size); - if (r->type->set(r, reg_params[i].value) != ERROR_OK) - return ERROR_FAIL; + + if (reg_params[i].direction == PARAM_OUT || reg_params[i].direction == PARAM_IN_OUT) { + if (r->type->set(r, reg_params[i].value) != ERROR_OK) + return ERROR_FAIL; + } } @@ -1059,7 +1765,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, /* Run algorithm */ LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point); - if (oldriscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK) + if (riscv_resume(target, 0, entry_point, 0, 0, true) != ERROR_OK) return ERROR_FAIL; int64_t start = timeval_ms(); @@ -1067,11 +1773,28 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, LOG_DEBUG("poll()"); int64_t now = timeval_ms(); if (now - start > timeout_ms) { - LOG_ERROR("Algorithm timed out after %d ms.", timeout_ms); - LOG_ERROR(" now = 0x%08x", (uint32_t) now); - LOG_ERROR(" start = 0x%08x", (uint32_t) start); - oldriscv_halt(target); + LOG_ERROR("Algorithm timed out after %" PRId64 " ms.", now - start); + riscv_halt(target); old_or_new_riscv_poll(target); + enum gdb_regno regnums[] = { + GDB_REGNO_RA, GDB_REGNO_SP, GDB_REGNO_GP, GDB_REGNO_TP, + GDB_REGNO_T0, GDB_REGNO_T1, GDB_REGNO_T2, GDB_REGNO_FP, + GDB_REGNO_S1, GDB_REGNO_A0, GDB_REGNO_A1, GDB_REGNO_A2, + GDB_REGNO_A3, GDB_REGNO_A4, GDB_REGNO_A5, GDB_REGNO_A6, + GDB_REGNO_A7, GDB_REGNO_S2, GDB_REGNO_S3, GDB_REGNO_S4, + GDB_REGNO_S5, GDB_REGNO_S6, GDB_REGNO_S7, GDB_REGNO_S8, + GDB_REGNO_S9, GDB_REGNO_S10, GDB_REGNO_S11, GDB_REGNO_T3, + GDB_REGNO_T4, GDB_REGNO_T5, GDB_REGNO_T6, + GDB_REGNO_PC, + GDB_REGNO_MSTATUS, GDB_REGNO_MEPC, GDB_REGNO_MCAUSE, + }; + for (unsigned i = 0; i < DIM(regnums); i++) { + enum gdb_regno regno = regnums[i]; + riscv_reg_t reg_value; + if (riscv_get_register(target, ®_value, regno) != ERROR_OK) + break; + LOG_ERROR("%s = 0x%" PRIx64, gdb_regno_name(regno), reg_value); + } return ERROR_TARGET_TIMEOUT; } @@ -1080,10 +1803,14 @@ 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_set_current_hartid(target, hartid) != ERROR_OK) + return ERROR_FAIL; + if (reg_pc->type->get(reg_pc) != ERROR_OK) return ERROR_FAIL; uint64_t final_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size); - if (final_pc != exit_point) { + if (exit_point && final_pc != exit_point) { LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%" TARGET_PRIxADDR, final_pc, exit_point); return ERROR_FAIL; @@ -1101,26 +1828,32 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, return ERROR_FAIL; for (int i = 0; i < num_reg_params; i++) { + if (reg_params[i].direction == PARAM_IN || + reg_params[i].direction == PARAM_IN_OUT) { + struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0); + if (r->type->get(r) != ERROR_OK) { + LOG_ERROR("get(%s) failed", r->name); + return ERROR_FAIL; + } + buf_cpy(r->value, reg_params[i].value, reg_params[i].size); + } LOG_DEBUG("restore %s", reg_params[i].reg_name); struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, 0); buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]); - if (r->type->set(r, buf) != ERROR_OK) + if (r->type->set(r, buf) != ERROR_OK) { + LOG_ERROR("set(%s) failed", r->name); return ERROR_FAIL; + } } return ERROR_OK; } -/* Should run code on the target to perform CRC of -memory. Not yet implemented. -*/ - static int riscv_checksum_memory(struct target *target, target_addr_t address, uint32_t count, uint32_t *checksum) { - *checksum = 0xFFFFFFFF; - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + return ERROR_FAIL; } /*** OpenOCD Helper Functions ***/ @@ -1149,15 +1882,16 @@ static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid) } else if (target->state != TARGET_RUNNING && !halted) { LOG_DEBUG(" triggered running"); target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; return RPH_DISCOVERED_RUNNING; } return RPH_NO_CHANGE; } -int set_debug_reason(struct target *target, int hartid) +int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason) { - switch (riscv_halt_reason(target, hartid)) { + switch (halt_reason) { case RISCV_HALT_BREAKPOINT: target->debug_reason = DBG_REASON_BREAKPOINT; break; @@ -1165,6 +1899,7 @@ int set_debug_reason(struct target *target, int hartid) 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: @@ -1176,6 +1911,7 @@ int set_debug_reason(struct target *target, int hartid) case RISCV_HALT_ERROR: return ERROR_FAIL; } + LOG_DEBUG("[%s] debug_reason=%d", target_name(target), target->debug_reason); return ERROR_OK; } @@ -1205,204 +1941,130 @@ int riscv_openocd_poll(struct target *target) } LOG_DEBUG(" hart %d halted", halted_hart); - /* If we're here then at least one hart triggered. That means - * we want to go and halt _every_ hart in the system, as that's - * the invariant we hold here. Some harts might have already - * halted (as we're either in single-step mode or they also - * triggered a breakpoint), so don't attempt to halt those - * harts. */ - for (int i = 0; i < riscv_count_harts(target); ++i) - riscv_halt_one_hart(target, i); + target->state = TARGET_HALTED; + enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart); + if (set_debug_reason(target, halt_reason) != ERROR_OK) + return ERROR_FAIL; + + target->rtos->current_threadid = halted_hart + 1; + target->rtos->current_thread = halted_hart + 1; + riscv_set_rtos_hartid(target, halted_hart); + + /* If we're here then at least one hart triggered. That means we want + * to go and halt _every_ hart (configured with -rtos riscv) in the + * system, as that's the invariant we hold here. Some harts might have + * already halted (as we're either in single-step mode or they also + * triggered a breakpoint), so don't attempt to halt those harts. + * riscv_halt() will do all that for us. */ + riscv_halt(target); } else if (target->smp) { - bool halt_discovered = false; - bool newly_halted[128] = {0}; + unsigned halts_discovered = 0; + unsigned total_targets = 0; + bool newly_halted[RISCV_MAX_HARTS] = {0}; + unsigned should_remain_halted = 0; + unsigned should_resume = 0; unsigned i = 0; for (struct target_list *list = target->head; list != NULL; list = list->next, i++) { + total_targets++; struct target *t = list->target; riscv_info_t *r = riscv_info(t); assert(i < DIM(newly_halted)); enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid); switch (out) { - case RPH_NO_CHANGE: - break; - case RPH_DISCOVERED_RUNNING: - t->state = TARGET_RUNNING; - break; - case RPH_DISCOVERED_HALTED: - halt_discovered = true; - newly_halted[i] = true; - t->state = TARGET_HALTED; - if (set_debug_reason(t, r->current_hartid) != ERROR_OK) - return ERROR_FAIL; - break; - case RPH_ERROR: + case RPH_NO_CHANGE: + break; + case RPH_DISCOVERED_RUNNING: + t->state = TARGET_RUNNING; + t->debug_reason = DBG_REASON_NOTHALTED; + break; + case RPH_DISCOVERED_HALTED: + halts_discovered++; + newly_halted[i] = true; + t->state = TARGET_HALTED; + enum riscv_halt_reason halt_reason = + riscv_halt_reason(t, r->current_hartid); + if (set_debug_reason(t, halt_reason) != ERROR_OK) return ERROR_FAIL; - } - } - if (halt_discovered) { - LOG_DEBUG("Halt other targets in this SMP group."); - i = 0; - for (struct target_list *list = target->head; list != NULL; - list = list->next, i++) { - struct target *t = list->target; - riscv_info_t *r = riscv_info(t); - if (t->state != TARGET_HALTED) { - if (riscv_halt_one_hart(t, r->current_hartid) != ERROR_OK) - return ERROR_FAIL; - t->state = TARGET_HALTED; - if (set_debug_reason(t, r->current_hartid) != ERROR_OK) - return ERROR_FAIL; - newly_halted[i] = true; + if (halt_reason == RISCV_HALT_BREAKPOINT) { + int retval; + switch (riscv_semihosting(t, &retval)) { + case SEMI_NONE: + case SEMI_WAITING: + /* This hart should remain halted. */ + should_remain_halted++; + break; + case SEMI_HANDLED: + /* This hart should be resumed, along with any other + * harts that halted due to haltgroups. */ + should_resume++; + break; + case SEMI_ERROR: + return retval; + } + } else if (halt_reason != RISCV_HALT_GROUP) { + should_remain_halted++; } - } - - /* Now that we have all our ducks in a row, tell the higher layers - * what just happened. */ - i = 0; - for (struct target_list *list = target->head; list != NULL; - list = list->next, i++) { - struct target *t = list->target; - if (newly_halted[i]) - target_call_event_callbacks(t, TARGET_EVENT_HALTED); - } - } - return ERROR_OK; - - } else { - enum riscv_poll_hart out = riscv_poll_hart(target, - riscv_current_hartid(target)); - if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) - return ERROR_OK; - else if (out == RPH_ERROR) - return ERROR_FAIL; - - halted_hart = riscv_current_hartid(target); - LOG_DEBUG(" hart %d halted", halted_hart); - } - - target->state = TARGET_HALTED; - if (set_debug_reason(target, halted_hart) != ERROR_OK) - return ERROR_FAIL; - - if (riscv_rtos_enabled(target)) { - target->rtos->current_threadid = halted_hart + 1; - target->rtos->current_thread = halted_hart + 1; - riscv_set_rtos_hartid(target, halted_hart); - } - - target->state = TARGET_HALTED; - - if (target->debug_reason == DBG_REASON_BREAKPOINT) { - int retval; - if (riscv_semihosting(target, &retval) != 0) - return retval; - } - - target_call_event_callbacks(target, TARGET_EVENT_HALTED); - return ERROR_OK; -} - -int riscv_openocd_halt(struct target *target) -{ - RISCV_INFO(r); - int result; - - LOG_DEBUG("[%d] halting all harts", target->coreid); + break; - if (target->smp) { - LOG_DEBUG("Halt other targets in this SMP group."); - struct target_list *targets = target->head; - result = ERROR_OK; - while (targets) { - struct target *t = targets->target; - targets = targets->next; - if (t->state != TARGET_HALTED) { - if (riscv_halt_all_harts(t) != ERROR_OK) - result = ERROR_FAIL; + case RPH_ERROR: + return ERROR_FAIL; } } - } else { - result = riscv_halt_all_harts(target); - } - - if (riscv_rtos_enabled(target)) { - if (r->rtos_hartid != -1) { - LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid); - target->rtos->current_threadid = r->rtos_hartid + 1; - target->rtos->current_thread = r->rtos_hartid + 1; - } else - LOG_DEBUG("halt requested, but no known RTOS hartid"); - } - - target->state = TARGET_HALTED; - target->debug_reason = DBG_REASON_DBGRQ; - target_call_event_callbacks(target, TARGET_EVENT_HALTED); - return result; -} -int riscv_openocd_resume( - struct target *target, - int current, - target_addr_t address, - int handle_breakpoints, - int debug_execution) -{ - LOG_DEBUG("debug_reason=%d", target->debug_reason); - - if (!current) - riscv_set_register(target, GDB_REGNO_PC, address); - - if (target->debug_reason == DBG_REASON_WATCHPOINT) { - /* To be able to run off a trigger, disable all the triggers, step, and - * then resume as usual. */ - struct watchpoint *watchpoint = target->watchpoints; - bool trigger_temporarily_cleared[RISCV_MAX_HWBPS] = {0}; - - int i = 0; - int result = ERROR_OK; - while (watchpoint && result == ERROR_OK) { - LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set); - trigger_temporarily_cleared[i] = watchpoint->set; - if (watchpoint->set) - result = riscv_remove_watchpoint(target, watchpoint); - watchpoint = watchpoint->next; - i++; + LOG_DEBUG("should_remain_halted=%d, should_resume=%d", + should_remain_halted, should_resume); + if (should_remain_halted && should_resume) { + LOG_WARNING("%d harts should remain halted, and %d should resume.", + should_remain_halted, should_resume); } + if (should_remain_halted) { + LOG_DEBUG("halt all"); + riscv_halt(target); + } else if (should_resume) { + LOG_DEBUG("resume all"); + riscv_resume(target, true, 0, 0, 0, false); + } + return ERROR_OK; - if (result == ERROR_OK) - result = riscv_step_rtos_hart(target); + } else { + enum riscv_poll_hart out = riscv_poll_hart(target, + riscv_current_hartid(target)); + if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) + return ERROR_OK; + else if (out == RPH_ERROR) + return ERROR_FAIL; - watchpoint = target->watchpoints; - i = 0; - while (watchpoint) { - LOG_DEBUG("watchpoint %d: cleared=%d", i, trigger_temporarily_cleared[i]); - if (trigger_temporarily_cleared[i]) { - if (result == ERROR_OK) - result = riscv_add_watchpoint(target, watchpoint); - else - riscv_add_watchpoint(target, watchpoint); - } - watchpoint = watchpoint->next; - i++; - } + halted_hart = riscv_current_hartid(target); + LOG_DEBUG(" hart %d halted", halted_hart); - if (result != ERROR_OK) - return result; + enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart); + if (set_debug_reason(target, halt_reason) != ERROR_OK) + return ERROR_FAIL; + target->state = TARGET_HALTED; } - int out = riscv_resume_all_harts(target); - if (out != ERROR_OK) { - LOG_ERROR("unable to resume all harts"); - return out; + if (target->debug_reason == DBG_REASON_BREAKPOINT) { + int retval; + switch (riscv_semihosting(target, &retval)) { + case SEMI_NONE: + case SEMI_WAITING: + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + break; + case SEMI_HANDLED: + if (riscv_resume(target, true, 0, 0, 0, false) != ERROR_OK) + return ERROR_FAIL; + break; + case SEMI_ERROR: + return retval; + } + } else { + target_call_event_callbacks(target, TARGET_EVENT_HALTED); } - register_cache_invalidate(target->reg_cache); - target->state = TARGET_RUNNING; - target_call_event_callbacks(target, TARGET_EVENT_RESUMED); - return out; + return ERROR_OK; } int riscv_openocd_step(struct target *target, int current, @@ -1413,6 +2075,10 @@ int riscv_openocd_step(struct target *target, int current, 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"); @@ -1420,6 +2086,10 @@ int riscv_openocd_step(struct target *target, int current, } register_cache_invalidate(target->reg_cache); + + if (enable_triggers(target, trigger_state) != ERROR_OK) + return ERROR_FAIL; + target->state = TARGET_RUNNING; target_call_event_callbacks(target, TARGET_EVENT_RESUMED); target->state = TARGET_HALTED; @@ -1491,6 +2161,16 @@ COMMAND_HANDLER(riscv_set_prefer_sba) return ERROR_OK; } +COMMAND_HANDLER(riscv_set_enable_virtual) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virtual); + return ERROR_OK; +} + void parse_error(const char *string, char c, unsigned position) { char buf[position+2]; @@ -1559,6 +2239,8 @@ int parse_ranges(range_t **ranges, const char **argv) if (pass == 0) { free(*ranges); *ranges = calloc(range + 2, sizeof(range_t)); + if (!*ranges) + return ERROR_FAIL; } else { (*ranges)[range].low = 1; (*ranges)[range].high = 0; @@ -1752,55 +2434,147 @@ COMMAND_HANDLER(riscv_set_ir) uint32_t value; COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); - if (!strcmp(CMD_ARGV[0], "idcode")) { + if (!strcmp(CMD_ARGV[0], "idcode")) buf_set_u32(ir_idcode, 0, 32, value); - return ERROR_OK; - } else if (!strcmp(CMD_ARGV[0], "dtmcs")) { + else if (!strcmp(CMD_ARGV[0], "dtmcs")) buf_set_u32(ir_dtmcontrol, 0, 32, value); - return ERROR_OK; - } else if (!strcmp(CMD_ARGV[0], "dmi")) { + else if (!strcmp(CMD_ARGV[0], "dmi")) buf_set_u32(ir_dbus, 0, 32, value); - return ERROR_OK; + else + return ERROR_FAIL; + + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_resume_order) +{ + if (CMD_ARGC > 1) { + LOG_ERROR("Command takes at most one argument"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (!strcmp(CMD_ARGV[0], "normal")) { + resume_order = RO_NORMAL; + } else if (!strcmp(CMD_ARGV[0], "reversed")) { + resume_order = RO_REVERSED; } else { + LOG_ERROR("Unsupported resume order: %s", CMD_ARGV[0]); return ERROR_FAIL; } + + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_use_bscan_tunnel) +{ + int irwidth = 0; + int tunnel_type = BSCAN_TUNNEL_NESTED_TAP; + + if (CMD_ARGC > 2) { + LOG_ERROR("Command takes at most two arguments"); + return ERROR_COMMAND_SYNTAX_ERROR; + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], irwidth); + } else if (CMD_ARGC == 2) { + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], irwidth); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tunnel_type); + } + if (tunnel_type == BSCAN_TUNNEL_NESTED_TAP) + LOG_INFO("Nested Tap based Bscan Tunnel Selected"); + else if (tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) + LOG_INFO("Simple Register based Bscan Tunnel Selected"); + else + LOG_INFO("Invalid Tunnel type selected ! : selecting default Nested Tap Type"); + + bscan_tunnel_type = tunnel_type; + bscan_tunnel_ir_width = irwidth; + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_set_enable_virt2phys) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virt2phys); + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_set_ebreakm) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreakm); + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_set_ebreaks) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaks); + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_set_ebreaku) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaku); + return ERROR_OK; } static const struct command_registration riscv_exec_command_handlers[] = { { .name = "test_compliance", .handler = riscv_test_compliance, + .usage = "", .mode = COMMAND_EXEC, - .usage = "riscv test_compliance", .help = "Runs a basic compliance test suite against the RISC-V Debug Spec." }, { .name = "set_command_timeout_sec", .handler = riscv_set_command_timeout_sec, .mode = COMMAND_ANY, - .usage = "riscv set_command_timeout_sec [sec]", + .usage = "[sec]", .help = "Set the wall-clock timeout (in seconds) for individual commands" }, { .name = "set_reset_timeout_sec", .handler = riscv_set_reset_timeout_sec, .mode = COMMAND_ANY, - .usage = "riscv set_reset_timeout_sec [sec]", + .usage = "[sec]", .help = "Set the wall-clock timeout (in seconds) after reset is deasserted" }, { .name = "set_prefer_sba", .handler = riscv_set_prefer_sba, .mode = COMMAND_ANY, - .usage = "riscv set_prefer_sba on|off", + .usage = "on|off", .help = "When on, prefer to use System Bus Access to access memory. " - "When off, prefer to use the Program Buffer to access memory." + "When off (default), prefer to use the Program Buffer to access memory." + }, + { + .name = "set_enable_virtual", + .handler = riscv_set_enable_virtual, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "When on, memory accesses are performed on physical or virtual " + "memory depending on the current system configuration. " + "When off (default), all memory accessses are performed on physical memory." }, { .name = "expose_csrs", .handler = riscv_set_expose_csrs, .mode = COMMAND_ANY, - .usage = "riscv expose_csrs n0[-m0][,n1[-m1]]...", + .usage = "n0[-m0][,n1[-m1]]...", .help = "Configure a list of inclusive ranges for CSRs to expose in " "addition to the standard ones. This must be executed before " "`init`." @@ -1809,7 +2583,7 @@ static const struct command_registration riscv_exec_command_handlers[] = { .name = "expose_custom", .handler = riscv_set_expose_custom, .mode = COMMAND_ANY, - .usage = "riscv expose_custom n0[-m0][,n1[-m1]]...", + .usage = "n0[-m0][,n1[-m1]]...", .help = "Configure a list of inclusive ranges for custom registers to " "expose. custom0 is accessed as abstract register number 0xc000, " "etc. This must be executed before `init`." @@ -1817,36 +2591,36 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "authdata_read", .handler = riscv_authdata_read, + .usage = "", .mode = COMMAND_ANY, - .usage = "riscv authdata_read", .help = "Return the 32-bit value read from authdata." }, { .name = "authdata_write", .handler = riscv_authdata_write, .mode = COMMAND_ANY, - .usage = "riscv authdata_write value", + .usage = "value", .help = "Write the 32-bit value to authdata." }, { .name = "dmi_read", .handler = riscv_dmi_read, .mode = COMMAND_ANY, - .usage = "riscv dmi_read address", + .usage = "address", .help = "Perform a 32-bit DMI read at address, returning the value." }, { .name = "dmi_write", .handler = riscv_dmi_write, .mode = COMMAND_ANY, - .usage = "riscv dmi_write address value", + .usage = "address value", .help = "Perform a 32-bit DMI write of value at address." }, { .name = "test_sba_config_reg", .handler = riscv_test_sba_config_reg, .mode = COMMAND_ANY, - .usage = "riscv test_sba_config_reg legal_address num_words " + .usage = "legal_address num_words " "illegal_address run_sbbusyerror_test[on/off]", .help = "Perform a series of tests on the SBCS register. " "Inputs are a legal, 128-byte aligned address and a number of words to " @@ -1859,19 +2633,71 @@ static const struct command_registration riscv_exec_command_handlers[] = { .name = "reset_delays", .handler = riscv_reset_delays, .mode = COMMAND_ANY, - .usage = "reset_delays [wait]", + .usage = "[wait]", .help = "OpenOCD learns how many Run-Test/Idle cycles are required " "between scans to avoid encountering the target being busy. This " "command resets those learned values after `wait` scans. It's only " "useful for testing OpenOCD itself." }, + { + .name = "resume_order", + .handler = riscv_resume_order, + .mode = COMMAND_ANY, + .usage = "normal|reversed", + .help = "Choose the order that harts are resumed in when `hasel` is not " + "supported. Normal order is from lowest hart index to highest. " + "Reversed order is from highest hart index to lowest." + }, { .name = "set_ir", .handler = riscv_set_ir, .mode = COMMAND_ANY, - .usage = "riscv set_ir_idcode [idcode|dtmcs|dmi] value", + .usage = "[idcode|dtmcs|dmi] value", .help = "Set IR value for specified JTAG register." }, + { + .name = "use_bscan_tunnel", + .handler = riscv_use_bscan_tunnel, + .mode = COMMAND_ANY, + .usage = "value [type]", + .help = "Enable or disable use of a BSCAN tunnel to reach DM. Supply " + "the width of the DM transport TAP's instruction register to " + "enable. Supply a value of 0 to disable. Pass A second argument " + "(optional) to indicate Bscan Tunnel Type {0:(default) NESTED_TAP , " + "1: DATA_REGISTER}" + }, + { + .name = "set_enable_virt2phys", + .handler = riscv_set_enable_virt2phys, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "When on (default), enable translation from virtual address to " + "physical address." + }, + { + .name = "set_ebreakm", + .handler = riscv_set_ebreakm, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "Control dcsr.ebreakm. When off, M-mode ebreak instructions " + "don't trap to OpenOCD. Defaults to on." + }, + { + .name = "set_ebreaks", + .handler = riscv_set_ebreaks, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "Control dcsr.ebreaks. When off, S-mode ebreak instructions " + "don't trap to OpenOCD. Defaults to on." + }, + { + .name = "set_ebreaku", + .handler = riscv_set_ebreaku, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions " + "don't trap to OpenOCD. Defaults to on." + }, COMMAND_REGISTRATION_DONE }; @@ -1908,7 +2734,7 @@ const struct command_registration riscv_command_handlers[] = { COMMAND_REGISTRATION_DONE }; -unsigned riscv_address_bits(struct target *target) +static unsigned riscv_xlen_nonconst(struct target *target) { return riscv_xlen(target); } @@ -1923,8 +2749,8 @@ struct target_type riscv_target = { /* poll current target status */ .poll = old_or_new_riscv_poll, - .halt = old_or_new_riscv_halt, - .resume = old_or_new_riscv_resume, + .halt = riscv_halt, + .resume = riscv_target_resume, .step = old_or_new_riscv_step, .assert_reset = riscv_assert_reset, @@ -1932,10 +2758,16 @@ struct target_type riscv_target = { .read_memory = riscv_read_memory, .write_memory = riscv_write_memory, + .read_phys_memory = riscv_read_phys_memory, + .write_phys_memory = riscv_write_phys_memory, .checksum_memory = riscv_checksum_memory, + .mmu = riscv_mmu, + .virt2phys = riscv_virt2phys, + .get_gdb_reg_list = riscv_get_gdb_reg_list, + .get_gdb_reg_list_noread = riscv_get_gdb_reg_list_noread, .add_breakpoint = riscv_add_breakpoint, .remove_breakpoint = riscv_remove_breakpoint, @@ -1950,7 +2782,7 @@ struct target_type riscv_target = { .commands = riscv_command_handlers, - .address_bits = riscv_address_bits + .address_bits = riscv_xlen_nonconst, }; /*** RISC-V Interface ***/ @@ -1964,72 +2796,52 @@ void riscv_info_init(struct target *target, riscv_info_t *r) memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id)); - for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) { + for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) r->xlen[h] = -1; - - for (size_t e = 0; e < RISCV_MAX_REGISTERS; ++e) - r->valid_saved_registers[h][e] = false; - } -} - -int riscv_halt_all_harts(struct target *target) -{ - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; - - riscv_halt_one_hart(target, i); - } - - riscv_invalidate_register_cache(target); - - return ERROR_OK; } -int riscv_halt_one_hart(struct target *target, int hartid) +static int riscv_resume_go_all_harts(struct target *target) { RISCV_INFO(r); - LOG_DEBUG("halting hart %d", hartid); - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) - return ERROR_FAIL; - if (riscv_is_halted(target)) { - LOG_DEBUG(" hart %d requested halt, but was already halted", hartid); - return ERROR_OK; - } - int result = r->halt_current_hart(target); - register_cache_invalidate(target->reg_cache); - return result; -} + /* Dummy variables to make mingw32-gcc happy. */ + int first = 0; + int last = 1; + int step = 1; + switch (resume_order) { + case RO_NORMAL: + first = 0; + last = riscv_count_harts(target) - 1; + step = 1; + break; + case RO_REVERSED: + first = riscv_count_harts(target) - 1; + last = 0; + step = -1; + break; + default: + assert(0); + } -int riscv_resume_all_harts(struct target *target) -{ - for (int i = 0; i < riscv_count_harts(target); ++i) { + for (int i = first; i != last + step; i += step) { if (!riscv_hart_enabled(target, i)) continue; - riscv_resume_one_hart(target, i); + LOG_DEBUG("resuming hart %d", i); + if (riscv_set_current_hartid(target, i) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + if (r->resume_go(target) != ERROR_OK) + return ERROR_FAIL; + } else { + LOG_DEBUG(" hart %d requested resume, but was already resumed", i); + } } riscv_invalidate_register_cache(target); return ERROR_OK; } -int riscv_resume_one_hart(struct target *target, int hartid) -{ - RISCV_INFO(r); - LOG_DEBUG("resuming hart %d", hartid); - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) - return ERROR_FAIL; - if (!riscv_is_halted(target)) { - LOG_DEBUG(" hart %d requested resume, but was already resumed", hartid); - return ERROR_OK; - } - - r->on_resume(target); - return r->resume_current_hart(target); -} - int riscv_step_rtos_hart(struct target *target) { RISCV_INFO(r); @@ -2075,7 +2887,7 @@ bool riscv_supports_extension(struct target *target, int hartid, char letter) return r->misa[hartid] & (1 << num); } -int riscv_xlen(const struct target *target) +unsigned riscv_xlen(const struct target *target) { return riscv_xlen_of_hart(target, riscv_current_hartid(target)); } @@ -2087,7 +2899,6 @@ int riscv_xlen_of_hart(const struct target *target, int hartid) return r->xlen[hartid]; } -extern struct rtos_type riscv_rtos; bool riscv_rtos_enabled(const struct target *target) { return false; @@ -2152,9 +2963,9 @@ int riscv_count_harts(struct target *target) if (target == NULL) return 1; RISCV_INFO(r); - if (r == NULL) + if (r == NULL || r->hart_count == NULL) return 1; - return r->hart_count; + return r->hart_count(target); } bool riscv_has_register(struct target *target, int hartid, int regid) @@ -2162,6 +2973,55 @@ bool riscv_has_register(struct target *target, int hartid, int regid) return 1; } +/** + * If write is true: + * return true iff we are guaranteed that the register will contain exactly + * the value we just wrote when it's read. + * If write is false: + * return true iff we are guaranteed that the register will read the same + * value in the future as the value we just read. + */ +static bool gdb_regno_cacheable(enum gdb_regno regno, bool write) +{ + /* GPRs, FPRs, vector registers are just normal data stores. */ + if (regno <= GDB_REGNO_XPR31 || + (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) || + (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)) + return true; + + /* Most CSRs won't change value on us, but we can't assume it about rbitrary + * CSRs. */ + switch (regno) { + case GDB_REGNO_DPC: + return true; + + case GDB_REGNO_VSTART: + case GDB_REGNO_VXSAT: + case GDB_REGNO_VXRM: + case GDB_REGNO_VLENB: + case GDB_REGNO_VL: + case GDB_REGNO_VTYPE: + case GDB_REGNO_MISA: + case GDB_REGNO_DCSR: + case GDB_REGNO_DSCRATCH0: + case GDB_REGNO_MSTATUS: + case GDB_REGNO_MEPC: + case GDB_REGNO_MCAUSE: + case GDB_REGNO_SATP: + /* + * WARL registers might not contain the value we just wrote, but + * these ones won't spontaneously change their value either. * + */ + return !write; + + case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */ + case GDB_REGNO_TDATA1: /* Changes value when tselect is changed. */ + case GDB_REGNO_TDATA2: /* Changse value when tselect is changed. */ + default: + return false; + } +} + /** * This function is called when the debug user wants to change the value of a * register. The new value may be cached, and may not be written until the hart @@ -2177,7 +3037,23 @@ int riscv_set_register_on_hart(struct target *target, int hartid, RISCV_INFO(r); LOG_DEBUG("{%d} %s <- %" PRIx64, hartid, gdb_regno_name(regid), value); assert(r->set_register); - return r->set_register(target, hartid, regid, value); + + /* TODO: Hack to deal with gdb that thinks these registers still exist. */ + if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 && + riscv_supports_extension(target, hartid, 'E')) + return ERROR_OK; + + struct reg *reg = &target->reg_cache->reg_list[regid]; + buf_set_u64(reg->value, 0, reg->size, value); + + int result = r->set_register(target, hartid, regid, value); + if (result == ERROR_OK) + reg->valid = gdb_regno_cacheable(regid, true); + else + reg->valid = false; + LOG_DEBUG("[%s]{%d} wrote 0x%" PRIx64 " to %s valid=%d", + target_name(target), hartid, value, reg->name, reg->valid); + return result; } int riscv_get_register(struct target *target, riscv_reg_t *value, @@ -2193,14 +3069,31 @@ int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value, RISCV_INFO(r); struct reg *reg = &target->reg_cache->reg_list[regid]; + if (!reg->exist) { + LOG_DEBUG("[%s]{%d} %s does not exist.", + target_name(target), hartid, gdb_regno_name(regid)); + return ERROR_FAIL; + } if (reg && reg->valid && hartid == riscv_current_hartid(target)) { *value = buf_get_u64(reg->value, 0, reg->size); + LOG_DEBUG("{%d} %s: %" PRIx64 " (cached)", hartid, + gdb_regno_name(regid), *value); + return ERROR_OK; + } + + /* TODO: Hack to deal with gdb that thinks these registers still exist. */ + if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && + riscv_supports_extension(target, hartid, 'E')) { + *value = 0; return ERROR_OK; } int result = r->get_register(target, value, hartid, regid); + if (result == ERROR_OK) + reg->valid = gdb_regno_cacheable(regid, false); + LOG_DEBUG("{%d} %s: %" PRIx64, hartid, gdb_regno_name(regid), *value); return result; } @@ -2311,7 +3204,9 @@ int riscv_enumerate_triggers(struct target *target) for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) { r->trigger_count[hartid] = t; - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t); + /* If we can't write tselect, then this hart does not support triggers. */ + if (riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t) != ERROR_OK) + break; uint64_t tselect_rb; result = riscv_get_register_on_hart(target, &tselect_rb, hartid, GDB_REGNO_TSELECT); @@ -2329,6 +3224,8 @@ int riscv_enumerate_triggers(struct target *target) 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 @@ -2357,10 +3254,68 @@ const char *gdb_regno_name(enum gdb_regno regno) 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: @@ -2381,12 +3336,86 @@ const char *gdb_regno_name(enum gdb_regno regno) return "dpc"; case GDB_REGNO_DCSR: return "dcsr"; - case GDB_REGNO_DSCRATCH: - return "dscratch"; + case GDB_REGNO_DSCRATCH0: + return "dscratch0"; case GDB_REGNO_MSTATUS: return "mstatus"; + case GDB_REGNO_MEPC: + return "mepc"; + case GDB_REGNO_MCAUSE: + return "mcause"; case GDB_REGNO_PRIV: return "priv"; + case GDB_REGNO_SATP: + return "satp"; + case GDB_REGNO_VTYPE: + return "vtype"; + case GDB_REGNO_VL: + return "vl"; + case GDB_REGNO_V0: + return "v0"; + case GDB_REGNO_V1: + return "v1"; + case GDB_REGNO_V2: + return "v2"; + case GDB_REGNO_V3: + return "v3"; + case GDB_REGNO_V4: + return "v4"; + case GDB_REGNO_V5: + return "v5"; + case GDB_REGNO_V6: + return "v6"; + case GDB_REGNO_V7: + return "v7"; + case GDB_REGNO_V8: + return "v8"; + case GDB_REGNO_V9: + return "v9"; + case GDB_REGNO_V10: + return "v10"; + case GDB_REGNO_V11: + return "v11"; + case GDB_REGNO_V12: + return "v12"; + case GDB_REGNO_V13: + return "v13"; + case GDB_REGNO_V14: + return "v14"; + case GDB_REGNO_V15: + return "v15"; + case GDB_REGNO_V16: + return "v16"; + case GDB_REGNO_V17: + return "v17"; + case GDB_REGNO_V18: + return "v18"; + case GDB_REGNO_V19: + return "v19"; + case GDB_REGNO_V20: + return "v20"; + case GDB_REGNO_V21: + return "v21"; + case GDB_REGNO_V22: + return "v22"; + case GDB_REGNO_V23: + return "v23"; + case GDB_REGNO_V24: + return "v24"; + case GDB_REGNO_V25: + return "v25"; + case GDB_REGNO_V26: + return "v26"; + case GDB_REGNO_V27: + return "v27"; + case GDB_REGNO_V28: + return "v28"; + case GDB_REGNO_V29: + return "v29"; + case GDB_REGNO_V30: + return "v30"; + case GDB_REGNO_V31: + return "v31"; default: if (regno <= GDB_REGNO_XPR31) sprintf(buf, "x%d", regno - GDB_REGNO_ZERO); @@ -2404,20 +3433,29 @@ static int register_get(struct reg *reg) { riscv_reg_info_t *reg_info = reg->arch_info; struct target *target = reg_info->target; - uint64_t value; - int result = riscv_get_register(target, &value, reg->number); - if (result != ERROR_OK) - return result; - buf_set_u64(reg->value, 0, reg->size, value); - /* CSRs (and possibly other extension) registers may change value at any - * time. */ - if (reg->number <= GDB_REGNO_XPR31 || - (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) || - reg->number == GDB_REGNO_PC) - reg->valid = true; - LOG_DEBUG("[%d]{%d} read 0x%" PRIx64 " from %s (valid=%d)", - target->coreid, riscv_current_hartid(target), value, reg->name, - reg->valid); + RISCV_INFO(r); + + if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) { + if (!r->get_register_buf) { + LOG_ERROR("Reading register %s not supported on this RISC-V target.", + gdb_regno_name(reg->number)); + return ERROR_FAIL; + } + + if (r->get_register_buf(target, reg->value, reg->number) != ERROR_OK) + return ERROR_FAIL; + } else { + uint64_t value; + int result = riscv_get_register(target, &value, reg->number); + if (result != ERROR_OK) + return result; + buf_set_u64(reg->value, 0, reg->size, value); + } + reg->valid = gdb_regno_cacheable(reg->number, false); + char *str = buf_to_str(reg->value, reg->size, 16); + LOG_DEBUG("[%d]{%d} read 0x%s from %s (valid=%d)", target->coreid, + riscv_current_hartid(target), str, reg->name, reg->valid); + free(str); return ERROR_OK; } @@ -2425,22 +3463,42 @@ 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_str(buf, reg->size, 16); + LOG_DEBUG("[%d]{%d} write 0x%s to %s (valid=%d)", target->coreid, + riscv_current_hartid(target), str, reg->name, reg->valid); + free(str); + + memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8)); + reg->valid = gdb_regno_cacheable(reg->number, true); + + if (reg->number == GDB_REGNO_TDATA1 || + reg->number == GDB_REGNO_TDATA2) { + r->manual_hwbp_set = true; + /* When enumerating triggers, we clear any triggers with DMODE set, + * assuming they were left over from a previous debug session. So make + * sure that is done before a user might be setting their own triggers. + */ + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + } + + if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) { + if (!r->set_register_buf) { + LOG_ERROR("Writing register %s not supported on this RISC-V target.", + gdb_regno_name(reg->number)); + return ERROR_FAIL; + } + + if (r->set_register_buf(target, reg->number, reg->value) != ERROR_OK) + return ERROR_FAIL; + } else { + uint64_t value = buf_get_u64(buf, 0, reg->size); + if (riscv_set_register(target, reg->number, value) != ERROR_OK) + return ERROR_FAIL; + } - uint64_t value = buf_get_u64(buf, 0, reg->size); - - LOG_DEBUG("[%d]{%d} write 0x%" PRIx64 " to %s (valid=%d)", - target->coreid, riscv_current_hartid(target), value, reg->name, - reg->valid); - struct reg *r = &target->reg_cache->reg_list[reg->number]; - /* CSRs (and possibly other extension) registers may change value at any - * time. */ - if (reg->number <= GDB_REGNO_XPR31 || - (reg->number >= GDB_REGNO_FPR0 && reg->number <= GDB_REGNO_FPR31) || - reg->number == GDB_REGNO_PC) - r->valid = true; - memcpy(r->value, buf, (r->size + 7) / 8); - - riscv_set_register(target, reg->number, value); return ERROR_OK; } @@ -2466,6 +3524,8 @@ int riscv_init_registers(struct target *target) 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; @@ -2483,13 +3543,19 @@ int riscv_init_registers(struct target *target) 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; + int hartid = riscv_current_hartid(target); + static struct reg_feature feature_cpu = { .name = "org.gnu.gdb.riscv.cpu" }; @@ -2499,6 +3565,9 @@ int riscv_init_registers(struct target *target) 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" }; @@ -2506,14 +3575,117 @@ int riscv_init_registers(struct target *target) .name = "org.gnu.gdb.riscv.custom" }; - static struct reg_data_type type_ieee_single = { - .type = REG_TYPE_IEEE_SINGLE, - .id = "ieee_single" + /* These types are built into gdb. */ + static struct reg_data_type type_ieee_single = { .type = REG_TYPE_IEEE_SINGLE, .id = "ieee_single" }; + static struct reg_data_type type_ieee_double = { .type = REG_TYPE_IEEE_DOUBLE, .id = "ieee_double" }; + static struct reg_data_type_union_field single_double_fields[] = { + {"float", &type_ieee_single, single_double_fields + 1}, + {"double", &type_ieee_double, NULL}, + }; + static struct reg_data_type_union single_double_union = { + .fields = single_double_fields }; - static struct reg_data_type type_ieee_double = { - .type = REG_TYPE_IEEE_DOUBLE, - .id = "ieee_double" + static struct reg_data_type type_ieee_single_double = { + .type = REG_TYPE_ARCH_DEFINED, + .id = "FPU_FD", + .type_class = REG_TYPE_CLASS_UNION, + .reg_type_union = &single_double_union }; + static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" }; + static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" }; + static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" }; + static struct reg_data_type type_uint64 = { .type = REG_TYPE_UINT64, .id = "uint64" }; + static struct reg_data_type type_uint128 = { .type = REG_TYPE_UINT128, .id = "uint128" }; + + /* This is roughly the XML we want: + * + * + * + * + * + * + * + * + * + * + * + * + */ + + info->vector_uint8.type = &type_uint8; + info->vector_uint8.count = info->vlenb[hartid]; + info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED; + info->type_uint8_vector.id = "bytes"; + info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR; + info->type_uint8_vector.reg_type_vector = &info->vector_uint8; + + info->vector_uint16.type = &type_uint16; + info->vector_uint16.count = info->vlenb[hartid] / 2; + info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED; + info->type_uint16_vector.id = "shorts"; + info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR; + info->type_uint16_vector.reg_type_vector = &info->vector_uint16; + + info->vector_uint32.type = &type_uint32; + info->vector_uint32.count = info->vlenb[hartid] / 4; + info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED; + info->type_uint32_vector.id = "words"; + info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR; + info->type_uint32_vector.reg_type_vector = &info->vector_uint32; + + info->vector_uint64.type = &type_uint64; + info->vector_uint64.count = info->vlenb[hartid] / 8; + info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED; + info->type_uint64_vector.id = "longs"; + info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR; + info->type_uint64_vector.reg_type_vector = &info->vector_uint64; + + info->vector_uint128.type = &type_uint128; + info->vector_uint128.count = info->vlenb[hartid] / 16; + info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED; + info->type_uint128_vector.id = "quads"; + info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR; + info->type_uint128_vector.reg_type_vector = &info->vector_uint128; + + info->vector_fields[0].name = "b"; + info->vector_fields[0].type = &info->type_uint8_vector; + if (info->vlenb[hartid] >= 2) { + info->vector_fields[0].next = info->vector_fields + 1; + info->vector_fields[1].name = "s"; + info->vector_fields[1].type = &info->type_uint16_vector; + } else { + info->vector_fields[0].next = NULL; + } + if (info->vlenb[hartid] >= 4) { + info->vector_fields[1].next = info->vector_fields + 2; + info->vector_fields[2].name = "w"; + info->vector_fields[2].type = &info->type_uint32_vector; + } else { + info->vector_fields[1].next = NULL; + } + if (info->vlenb[hartid] >= 8) { + info->vector_fields[2].next = info->vector_fields + 3; + info->vector_fields[3].name = "l"; + info->vector_fields[3].type = &info->type_uint64_vector; + } else { + info->vector_fields[2].next = NULL; + } + if (info->vlenb[hartid] >= 16) { + info->vector_fields[3].next = info->vector_fields + 4; + info->vector_fields[4].name = "q"; + info->vector_fields[4].type = &info->type_uint128_vector; + } else { + info->vector_fields[3].next = NULL; + } + info->vector_fields[4].next = NULL; + + info->vector_union.fields = info->vector_fields; + + info->type_vector.type = REG_TYPE_ARCH_DEFINED; + info->type_vector.id = "riscv_vector"; + info->type_vector.type_class = REG_TYPE_CLASS_UNION; + info->type_vector.reg_type_union = &info->vector_union; + struct csr_info csr_info[] = { #define DECLARE_CSR(name, number) { number, #name }, #include "encoding.h" @@ -2527,6 +3699,8 @@ int riscv_init_registers(struct target *target) int custom_within_range = 0; riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t)); + if (!shared_reg_info) + return ERROR_FAIL; shared_reg_info->target = target; /* When gdb requests register N, gdb_get_register_packet() assumes that this @@ -2547,6 +3721,11 @@ int riscv_init_registers(struct target *target) * target is in theory allowed to change XLEN on us. But I expect a lot * of other things to break in that case as well. */ if (number <= GDB_REGNO_XPR31) { + r->exist = number <= GDB_REGNO_XPR15 || + !riscv_supports_extension(target, hartid, 'E'); + /* TODO: For now we fake that all GPRs exist because otherwise gdb + * doesn't work. */ + r->exist = true; r->caller_save = true; switch (number) { case GDB_REGNO_ZERO: @@ -2655,12 +3834,13 @@ int riscv_init_registers(struct target *target) r->feature = &feature_cpu; } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { r->caller_save = true; - if (riscv_supports_extension(target, riscv_current_hartid(target), - 'D')) { - r->reg_data_type = &type_ieee_double; + if (riscv_supports_extension(target, hartid, 'D')) { r->size = 64; - } else if (riscv_supports_extension(target, - riscv_current_hartid(target), 'F')) { + if (riscv_supports_extension(target, hartid, 'F')) + r->reg_data_type = &type_ieee_single_double; + else + r->reg_data_type = &type_ieee_double; + } else if (riscv_supports_extension(target, hartid, 'F')) { r->reg_data_type = &type_ieee_single; r->size = 32; } else { @@ -2791,8 +3971,7 @@ int riscv_init_registers(struct target *target) case CSR_FFLAGS: case CSR_FRM: case CSR_FCSR: - r->exist = riscv_supports_extension(target, - riscv_current_hartid(target), 'F'); + r->exist = riscv_supports_extension(target, hartid, 'F'); r->group = "float"; r->feature = &feature_fpu; break; @@ -2806,18 +3985,19 @@ int riscv_init_registers(struct target *target) case CSR_SCAUSE: case CSR_STVAL: case CSR_SATP: - r->exist = riscv_supports_extension(target, - riscv_current_hartid(target), 'S'); + r->exist = riscv_supports_extension(target, hartid, 'S'); break; case CSR_MEDELEG: case CSR_MIDELEG: /* "In systems with only M-mode, or with both M-mode and * U-mode but without U-mode trap support, the medeleg and * mideleg registers should not exist." */ - r->exist = riscv_supports_extension(target, riscv_current_hartid(target), 'S') || - riscv_supports_extension(target, riscv_current_hartid(target), 'N'); + r->exist = riscv_supports_extension(target, hartid, 'S') || + riscv_supports_extension(target, hartid, 'N'); break; + case CSR_PMPCFG1: + case CSR_PMPCFG3: case CSR_CYCLEH: case CSR_TIMEH: case CSR_INSTRETH: @@ -2883,6 +4063,15 @@ int riscv_init_registers(struct target *target) case CSR_MHPMCOUNTER31H: r->exist = riscv_xlen(target) == 32; break; + + case CSR_VSTART: + case CSR_VXSAT: + case CSR_VXRM: + case CSR_VL: + case CSR_VTYPE: + case CSR_VLENB: + r->exist = riscv_supports_extension(target, hartid, 'V'); + break; } if (!r->exist && expose_csr) { @@ -2901,7 +4090,16 @@ int riscv_init_registers(struct target *target) r->feature = &feature_virtual; r->size = 8; - } else { + } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) { + r->caller_save = false; + r->exist = riscv_supports_extension(target, hartid, 'V') && info->vlenb[hartid]; + r->size = info->vlenb[hartid] * 8; + sprintf(reg_name, "v%d", number - GDB_REGNO_V0); + r->group = "vector"; + r->feature = &feature_vector; + r->reg_data_type = &info->type_vector; + + } else if (number >= GDB_REGNO_COUNT) { /* Custom registers. */ assert(expose_custom); @@ -2912,7 +4110,8 @@ int riscv_init_registers(struct target *target) r->group = "custom"; r->feature = &feature_custom; r->arch_info = calloc(1, sizeof(riscv_reg_info_t)); - assert(r->arch_info); + if (!r->arch_info) + return ERROR_FAIL; ((riscv_reg_info_t *) r->arch_info)->target = target; ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number; sprintf(reg_name, "custom%d", custom_number); @@ -2934,3 +4133,43 @@ int riscv_init_registers(struct target *target) return ERROR_OK; } + + +void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field, + riscv_bscan_tunneled_scan_context_t *ctxt) +{ + jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE); + + memset(ctxt->tunneled_dr, 0, sizeof(ctxt->tunneled_dr)); + if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) { + ctxt->tunneled_dr[3].num_bits = 1; + ctxt->tunneled_dr[3].out_value = bscan_one; + ctxt->tunneled_dr[2].num_bits = 7; + ctxt->tunneled_dr_width = field->num_bits; + ctxt->tunneled_dr[2].out_value = &ctxt->tunneled_dr_width; + /* for BSCAN tunnel, there is a one-TCK skew between shift in and shift out, so + scanning num_bits + 1, and then will right shift the input field after executing the queues */ + + ctxt->tunneled_dr[1].num_bits = field->num_bits + 1; + ctxt->tunneled_dr[1].out_value = field->out_value; + ctxt->tunneled_dr[1].in_value = field->in_value; + + ctxt->tunneled_dr[0].num_bits = 3; + ctxt->tunneled_dr[0].out_value = bscan_zero; + } else { + /* BSCAN_TUNNEL_NESTED_TAP */ + ctxt->tunneled_dr[0].num_bits = 1; + ctxt->tunneled_dr[0].out_value = bscan_one; + ctxt->tunneled_dr[1].num_bits = 7; + ctxt->tunneled_dr_width = field->num_bits; + ctxt->tunneled_dr[1].out_value = &ctxt->tunneled_dr_width; + /* for BSCAN tunnel, there is a one-TCK skew between shift in and shift out, so + scanning num_bits + 1, and then will right shift the input field after executing the queues */ + ctxt->tunneled_dr[2].num_bits = field->num_bits + 1; + ctxt->tunneled_dr[2].out_value = field->out_value; + ctxt->tunneled_dr[2].in_value = field->in_value; + ctxt->tunneled_dr[3].num_bits = 3; + ctxt->tunneled_dr[3].out_value = bscan_zero; + } + jtag_add_dr_scan(target->tap, ARRAY_SIZE(ctxt->tunneled_dr), ctxt->tunneled_dr, TAP_IDLE); +} diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index ba50d2c514..7e74cf7304 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + #ifndef RISCV_H #define RISCV_H @@ -6,9 +8,11 @@ struct riscv_program; #include #include "opcodes.h" #include "gdb_regs.h" +#include "jtag/jtag.h" +#include "target/register.h" /* The register cache is statically allocated. */ -#define RISCV_MAX_HARTS 32 +#define RISCV_MAX_HARTS 1024 #define RISCV_MAX_REGISTERS 5000 #define RISCV_MAX_TRIGGERS 32 #define RISCV_MAX_HWBPS 16 @@ -16,6 +20,12 @@ struct riscv_program; #define DEFAULT_COMMAND_TIMEOUT_SEC 2 #define DEFAULT_RESET_TIMEOUT_SEC 30 +#define RISCV_SATP_MODE(xlen) ((xlen) == 32 ? SATP32_MODE : SATP64_MODE) +#define RISCV_SATP_PPN(xlen) ((xlen) == 32 ? SATP32_PPN : SATP64_PPN) +#define RISCV_PGSHIFT 12 + +# define PG_MAX_LEVEL 4 + extern struct target_type riscv011_target; extern struct target_type riscv013_target; @@ -32,6 +42,7 @@ enum riscv_halt_reason { RISCV_HALT_SINGLESTEP, RISCV_HALT_TRIGGER, RISCV_HALT_UNKNOWN, + RISCV_HALT_GROUP, RISCV_HALT_ERROR }; @@ -46,9 +57,6 @@ typedef struct { struct command_context *cmd_ctx; void *version_specific; - /* The number of harts on this system. */ - int hart_count; - /* The hart that the RTOS thinks is currently being debugged. */ int rtos_hartid; @@ -58,11 +66,6 @@ typedef struct { * every function than an actual */ int current_hartid; - /* Enough space to store all the registers we might need to save. */ - /* FIXME: This should probably be a bunch of register caches. */ - uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS]; - bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS]; - /* OpenOCD's register cache points into here. This is not per-hart because * we just invalidate the entire cache when we change which hart is * selected. */ @@ -75,6 +78,8 @@ typedef struct { /* It's possible that each core has a different supported ISA set. */ int xlen[RISCV_MAX_HARTS]; riscv_reg_t misa[RISCV_MAX_HARTS]; + /* Cached value of vlenb. 0 if vlenb is not readable for some reason. */ + unsigned vlenb[RISCV_MAX_HARTS]; /* The number of triggers per hart. */ unsigned trigger_count[RISCV_MAX_HARTS]; @@ -100,19 +105,33 @@ typedef struct { * delays, causing them to be relearned. Used for testing. */ int reset_delays_wait; + /* This target has been prepped and is ready to step/resume. */ + bool prepped; + /* This target was selected using hasel. */ + bool selected; + /* Helper functions that target the various RISC-V debug spec * implementations. */ int (*get_register)(struct target *target, riscv_reg_t *value, int hid, int rid); int (*set_register)(struct target *target, int hartid, 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 (*halt_current_hart)(struct target *target); - int (*resume_current_hart)(struct target *target); + /* 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); - int (*on_resume)(struct target *target); + /* Get this target as ready as possible to resume, without actually + * resuming. */ + int (*resume_prep)(struct target *target); + int (*halt_prep)(struct target *target); + 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 index, @@ -134,8 +153,52 @@ typedef struct { uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test); int (*test_compliance)(struct target *target); + + int (*read_memory)(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment); + + /* How many harts are attached to the DM that this target is attached to? */ + int (*hart_count)(struct target *target); + unsigned (*data_bits)(struct target *target); + + /* Storage for vector register types. */ + struct reg_data_type_vector vector_uint8; + struct reg_data_type_vector vector_uint16; + struct reg_data_type_vector vector_uint32; + struct reg_data_type_vector vector_uint64; + struct reg_data_type_vector vector_uint128; + struct reg_data_type type_uint8_vector; + struct reg_data_type type_uint16_vector; + struct reg_data_type type_uint32_vector; + struct reg_data_type type_uint64_vector; + struct reg_data_type type_uint128_vector; + struct reg_data_type_union_field vector_fields[5]; + 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; } riscv_info_t; +typedef struct { + uint8_t tunneled_dr_width; + struct scan_field tunneled_dr[4]; +} riscv_bscan_tunneled_scan_context_t; + +typedef struct { + const char *name; + int level; + unsigned va_bits; + unsigned pte_shift; + unsigned vpn_shift[PG_MAX_LEVEL]; + unsigned vpn_mask[PG_MAX_LEVEL]; + unsigned pte_ppn_shift[PG_MAX_LEVEL]; + unsigned pte_ppn_mask[PG_MAX_LEVEL]; + unsigned pa_ppn_shift[PG_MAX_LEVEL]; + unsigned pa_ppn_mask[PG_MAX_LEVEL]; +} virt2phys_info_t; + /* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/ extern int riscv_command_timeout_sec; @@ -144,6 +207,11 @@ extern int riscv_reset_timeout_sec; extern bool riscv_prefer_sba; +extern bool riscv_enable_virtual; +extern bool riscv_ebreakm; +extern bool riscv_ebreaks; +extern bool riscv_ebreaku; + /* Everything needs the RISC-V specific info structure, so here's a nice macro * that provides that. */ static inline riscv_info_t *riscv_info(const struct target *target) __attribute__((unused)); @@ -158,17 +226,28 @@ extern struct scan_field select_dbus; extern uint8_t ir_idcode[4]; extern struct scan_field select_idcode; +extern struct scan_field select_user4; +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 bscan_tunnel_type_t bscan_tunnel_type; + +uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out); +void select_dmi_via_bscan(struct target *target); + /*** OpenOCD Interface */ int riscv_openocd_poll(struct target *target); -int riscv_openocd_halt(struct target *target); +int riscv_halt(struct target *target); -int riscv_openocd_resume( +int riscv_resume( struct target *target, int current, target_addr_t address, int handle_breakpoints, - int debug_execution + int debug_execution, + bool single_hart ); int riscv_openocd_step( @@ -186,14 +265,6 @@ int riscv_openocd_deassert_reset(struct target *target); /* Initializes the shared RISC-V structure. */ void riscv_info_init(struct target *target, riscv_info_t *r); -/* Run control, possibly for multiple harts. The _all_harts versions resume - * all the enabled harts, which when running in RTOS mode is all the harts on - * the system. */ -int riscv_halt_all_harts(struct target *target); -int riscv_halt_one_hart(struct target *target, int hartid); -int riscv_resume_all_harts(struct target *target); -int riscv_resume_one_hart(struct target *target, int hartid); - /* Steps the hart that's currently selected in the RTOS, or if there is no RTOS * then the only hart. */ int riscv_step_rtos_hart(struct target *target); @@ -201,7 +272,7 @@ int riscv_step_rtos_hart(struct target *target); bool riscv_supports_extension(struct target *target, int hartid, char letter); /* Returns XLEN for the given (or current) hart. */ -int riscv_xlen(const struct target *target); +unsigned riscv_xlen(const struct target *target); int riscv_xlen_of_hart(const struct target *target, int hartid); bool riscv_rtos_enabled(const struct target *target); @@ -226,12 +297,14 @@ int riscv_count_harts(struct target *target); /* Returns TRUE if the target has the given register on the given hart. */ bool riscv_has_register(struct target *target, int hartid, int regid); -/* Returns the value of the given register on the given hart. 32-bit registers - * are zero extended to 64 bits. */ +/** Set register, updating the cache. */ int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v); +/** Set register, updating the cache. */ int riscv_set_register_on_hart(struct target *target, int hid, enum gdb_regno rid, uint64_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); +/** Get register, from the cache if it's in there. */ int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value, int hartid, enum gdb_regno regid); @@ -272,6 +345,15 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_addre int riscv_init_registers(struct target *target); void riscv_semihosting_init(struct target *target); -int riscv_semihosting(struct target *target, int *retval); +typedef enum { + SEMI_NONE, /* Not halted for a semihosting call. */ + SEMI_HANDLED, /* Call handled, and target was resumed. */ + SEMI_WAITING, /* Call handled, target is halted waiting until we can resume. */ + SEMI_ERROR /* Something went wrong. */ +} semihosting_result_t; +semihosting_result_t riscv_semihosting(struct target *target, int *retval); + +void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field, + riscv_bscan_tunneled_scan_context_t *ctxt); #endif diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c index c4b6653729..99d6c7713e 100644 --- a/src/target/riscv/riscv_semihosting.c +++ b/src/target/riscv/riscv_semihosting.c @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /*************************************************************************** * Copyright (C) 2018 by Liviu Ionescu * * ilg@livius.net * @@ -60,35 +62,35 @@ void riscv_semihosting_init(struct target *target) /** * Check for and process a semihosting request using the ARM protocol). This * is meant to be called when the target is stopped due to a debug mode entry. - * If the value 0 is returned then there was nothing to process. A non-zero - * return value signifies that a request was processed and the target resumed, - * or an error was encountered, in which case the caller must return - * immediately. * * @param target Pointer to the target to process. * @param retval Pointer to a location where the return code will be stored * @return non-zero value if a request was processed or an error encountered */ -int riscv_semihosting(struct target *target, int *retval) +semihosting_result_t riscv_semihosting(struct target *target, int *retval) { struct semihosting *semihosting = target->semihosting; - if (!semihosting) - return 0; + if (!semihosting) { + LOG_DEBUG(" -> NONE (!semihosting)"); + return SEMI_NONE; + } - if (!semihosting->is_active) - return 0; + if (!semihosting->is_active) { + LOG_DEBUG(" -> NONE (!semihosting->is_active)"); + return SEMI_NONE; + } - riscv_reg_t dpc; - int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC); + riscv_reg_t pc; + int result = riscv_get_register(target, &pc, GDB_REGNO_PC); if (result != ERROR_OK) - return 0; + return SEMI_ERROR; uint8_t tmp[12]; /* Read the current instruction, including the bracketing */ - *retval = target_read_memory(target, dpc - 4, 2, 6, tmp); + *retval = target_read_memory(target, pc - 4, 2, 6, tmp); if (*retval != ERROR_OK) - return 0; + return SEMI_ERROR; /* * The instructions that trigger a semihosting call, @@ -101,12 +103,12 @@ int riscv_semihosting(struct target *target, int *retval) uint32_t pre = target_buffer_get_u32(target, tmp); uint32_t ebreak = target_buffer_get_u32(target, tmp + 4); uint32_t post = target_buffer_get_u32(target, tmp + 8); - LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc); + 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. */ - return 0; + LOG_DEBUG(" -> NONE (no magic)"); + return SEMI_NONE; } /* @@ -114,18 +116,21 @@ int riscv_semihosting(struct target *target, int *retval) * operation to complete. */ if (!semihosting->hit_fileio) { - /* RISC-V uses A0 and A1 to pass function arguments */ riscv_reg_t r0; riscv_reg_t r1; result = riscv_get_register(target, &r0, GDB_REGNO_A0); - if (result != ERROR_OK) - return 0; + if (result != ERROR_OK) { + LOG_DEBUG(" -> ERROR (couldn't read a0)"); + return SEMI_ERROR; + } result = riscv_get_register(target, &r1, GDB_REGNO_A1); - if (result != ERROR_OK) - return 0; + if (result != ERROR_OK) { + LOG_DEBUG(" -> ERROR (couldn't read a1)"); + return SEMI_ERROR; + } semihosting->op = r0; semihosting->param = r1; @@ -136,11 +141,12 @@ int riscv_semihosting(struct target *target, int *retval) *retval = semihosting_common(target); if (*retval != ERROR_OK) { LOG_ERROR("Failed semihosting operation"); - return 0; + return SEMI_ERROR; } } else { /* Unknown operation number, not a semihosting call. */ - return 0; + LOG_DEBUG(" -> NONE (unknown operation number)"); + return SEMI_NONE; } } @@ -150,16 +156,16 @@ int riscv_semihosting(struct target *target, int *retval) */ if (semihosting->is_resumable && !semihosting->hit_fileio) { /* Resume right after the EBREAK 4 bytes instruction. */ - *retval = target_resume(target, 0, dpc+4, 0, 0); - if (*retval != ERROR_OK) { - LOG_ERROR("Failed to resume target"); - return 0; - } + *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4); + if (*retval != ERROR_OK) + return SEMI_ERROR; - return 1; + LOG_DEBUG(" -> HANDLED"); + return SEMI_HANDLED; } - return 0; + LOG_DEBUG(" -> WAITING"); + return SEMI_WAITING; } /* ------------------------------------------------------------------------- @@ -171,7 +177,7 @@ int riscv_semihosting(struct target *target, int *retval) */ static int riscv_semihosting_setup(struct target *target, int enable) { - LOG_DEBUG("enable=%d", enable); + LOG_DEBUG("[%s] enable=%d", target_name(target), enable); struct semihosting *semihosting = target->semihosting; if (semihosting) -- 2.30.2