From b3f052ba9d9582fe203ff8baa73fef66c02196ac Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 22 Sep 2021 18:39:57 +0200 Subject: [PATCH 01/15] jtag/core: fix unused assignment Clang scan-build complains about a variable assigned but never used. Although the value stored to 'val' is used in the enclosing expression, the value is never actually read from 'val' Remove the dead assignment. While there, reduce the scope of the variable by declaring the variable at the point of first use. Change-Id: Ibe2b55a7d70597833cfa7f3d843e7c3d2407f2df Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6587 Tested-by: jenkins --- src/jtag/core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/jtag/core.c b/src/jtag/core.c index 7da2a6c35..51875a00a 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -1336,7 +1336,6 @@ static int jtag_validate_ircapture(void) struct jtag_tap *tap; uint8_t *ir_test = NULL; struct scan_field field; - uint64_t val; int chain_pos = 0; int retval; @@ -1396,7 +1395,7 @@ static int jtag_validate_ircapture(void) */ if (tap->ir_length == 0) { tap->ir_length = 2; - while ((val = buf_get_u64(ir_test, chain_pos, tap->ir_length + 1)) == 1 + while (buf_get_u64(ir_test, chain_pos, tap->ir_length + 1) == 1 && tap->ir_length < JTAG_IRLEN_MAX) { tap->ir_length++; } @@ -1412,7 +1411,7 @@ static int jtag_validate_ircapture(void) * this part of the JTAG spec, so their capture mask/value * attributes might disable this test. */ - val = buf_get_u64(ir_test, chain_pos, tap->ir_length); + uint64_t val = buf_get_u64(ir_test, chain_pos, tap->ir_length); if ((val & tap->ir_capture_mask) != tap->ir_capture_value) { LOG_ERROR("%s: IR capture error; saw 0x%0*" PRIx64 " not 0x%0*" PRIx32, jtag_tap_name(tap), @@ -1428,7 +1427,7 @@ static int jtag_validate_ircapture(void) } /* verify the '11' sentinel we wrote is returned at the end */ - val = buf_get_u64(ir_test, chain_pos, 2); + uint64_t val = buf_get_u64(ir_test, chain_pos, 2); if (val != 0x3) { char *cbuf = buf_to_hex_str(ir_test, total_ir_length); From 57262ebeae7be08ba260c36bb11630e03e594856 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 22 Sep 2021 18:51:26 +0200 Subject: [PATCH 02/15] arm_tpiu_swo: fix two dead assignments Clang scan-build complains for two dead assignments: Value stored to 'retval' is never read Since the timer callback should not return error, print an error message if the data cannot be send out. Add a FIXME comment because in current code there is no string/name to report which connection has failed. In command tpiu enable check the returned value and propagate the error. Change-Id: I9a89e4c4f7b677e8222b2df09a31b2478ac9ca4f Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6588 Tested-by: jenkins --- src/target/arm_tpiu_swo.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/target/arm_tpiu_swo.c b/src/target/arm_tpiu_swo.c index f2b514826..024521364 100644 --- a/src/target/arm_tpiu_swo.c +++ b/src/target/arm_tpiu_swo.c @@ -155,7 +155,7 @@ static int arm_tpiu_swo_poll_trace(void *priv) if (obj->out_filename && obj->out_filename[0] == ':') list_for_each_entry(c, &obj->connections, lh) if (connection_write(c->connection, buf, size) != (int)size) - retval = ERROR_FAIL; + LOG_ERROR("Error writing to connection"); /* FIXME: which connection? */ return ERROR_OK; } @@ -678,6 +678,10 @@ static int jim_arm_tpiu_swo_enable(Jim_Interp *interp, int argc, Jim_Obj *const if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_SYNC) { retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_SSPSR_OFFSET, &value); + if (retval != ERROR_OK) { + LOG_ERROR("Cannot read TPIU register SSPSR"); + return JIM_ERR; + } if (!(value & BIT(obj->port_width - 1))) { LOG_ERROR("TPIU does not support port-width of %d bits", obj->port_width); return JIM_ERR; From 48d74f97114a76563753918da8708d45098fbf1d Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 22 Sep 2021 18:59:38 +0200 Subject: [PATCH 03/15] arm_semihosting: fix two dead assignments Clang scan-build complains for two dead assignments: Value stored to 'r' is never read Use the variable in the following line, instead of re-computing the pointer. Change-Id: I5d4069872be9da85fb28bbe0a82020b90f1efe46 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6589 Tested-by: jenkins --- src/target/arm_semihosting.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c index 1e5b5e252..792474acf 100644 --- a/src/target/arm_semihosting.c +++ b/src/target/arm_semihosting.c @@ -315,7 +315,7 @@ int arm_semihosting(struct target *target, int *retval) return 0; } else if (arm->core_state == ARM_STATE_ARM) { r = arm->pc; - pc = buf_get_u32(arm->pc->value, 0, 32); + pc = buf_get_u32(r->value, 0, 32); /* A32 instruction => check for HLT 0xF000 (0xE10F0070) */ uint32_t insn = 0; @@ -330,7 +330,7 @@ int arm_semihosting(struct target *target, int *retval) return 0; } else if (arm->core_state == ARM_STATE_THUMB) { r = arm->pc; - pc = buf_get_u32(arm->pc->value, 0, 32); + pc = buf_get_u32(r->value, 0, 32); /* T32 instruction => check for HLT 0x3C (0xBABC) */ uint16_t insn = 0; From 01de751025cfaebb7c1090f1d1ff00071ce39c71 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 22 Sep 2021 19:09:29 +0200 Subject: [PATCH 04/15] armv8: fix five dead assignments Clang scan-build complains for five dead assignments: Value stored to 'retval' is never read Check the returned value and propagate the error. Change-Id: I01172887a056d6f39ddcf2807848423970db1e89 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6590 Tested-by: jenkins --- src/target/armv8.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/target/armv8.c b/src/target/armv8.c index 749ea8729..26116bb33 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -454,29 +454,31 @@ static int armv8_read_reg_simdfp_aarch32(struct armv8_common *armv8, int regnum, retval = dpm->instr_read_data_r0(dpm, ARMV4_5_VMOV(1, 1, 0, (num >> 4), (num & 0xf)), &value_r0); + if (retval != ERROR_OK) + return retval; /* read r1 via dcc */ retval = dpm->instr_read_data_dcc(dpm, ARMV4_5_MCR(14, 0, 1, 0, 5, 0), &value_r1); - if (retval == ERROR_OK) { - *lvalue = value_r1; - *lvalue = ((*lvalue) << 32) | value_r0; - } else + if (retval != ERROR_OK) return retval; + *lvalue = value_r1; + *lvalue = ((*lvalue) << 32) | value_r0; num++; /* repeat above steps for high 64 bits of V register */ retval = dpm->instr_read_data_r0(dpm, ARMV4_5_VMOV(1, 1, 0, (num >> 4), (num & 0xf)), &value_r0); + if (retval != ERROR_OK) + return retval; retval = dpm->instr_read_data_dcc(dpm, ARMV4_5_MCR(14, 0, 1, 0, 5, 0), &value_r1); - if (retval == ERROR_OK) { - *hvalue = value_r1; - *hvalue = ((*hvalue) << 32) | value_r0; - } else + if (retval != ERROR_OK) return retval; + *hvalue = value_r1; + *hvalue = ((*hvalue) << 32) | value_r0; break; default: retval = ERROR_FAIL; @@ -586,12 +588,16 @@ static int armv8_write_reg_simdfp_aarch32(struct armv8_common *armv8, int regnum retval = dpm->instr_write_data_dcc(dpm, ARMV4_5_MRC(14, 0, 1, 0, 5, 0), value_r1); + if (retval != ERROR_OK) + return retval; /* write value_r0 to r0 via dcc then, * move to double word register from r0:r1: "vmov vm, r0, r1" */ retval = dpm->instr_write_data_r0(dpm, ARMV4_5_VMOV(0, 1, 0, (num >> 4), (num & 0xf)), value_r0); + if (retval != ERROR_OK) + return retval; num++; /* repeat above steps for high 64 bits of V register */ @@ -600,6 +606,8 @@ static int armv8_write_reg_simdfp_aarch32(struct armv8_common *armv8, int regnum retval = dpm->instr_write_data_dcc(dpm, ARMV4_5_MRC(14, 0, 1, 0, 5, 0), value_r1); + if (retval != ERROR_OK) + return retval; retval = dpm->instr_write_data_r0(dpm, ARMV4_5_VMOV(0, 1, 0, (num >> 4), (num & 0xf)), value_r0); From 327040ad745300c6c2117b81c584ff868567ef83 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 22 Sep 2021 19:14:29 +0200 Subject: [PATCH 05/15] target/lakemont: fix dead assignment Clang scan-build complains for a dead assignment: Value stored to 'tapstatus' is never read Remove the assignment and add a comment to point for a potential removal of the line. Change-Id: Iad2fdc7e6faf650e24cc086ee74c745acb0d1c73 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6591 Tested-by: jenkins --- src/target/lakemont.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/target/lakemont.c b/src/target/lakemont.c index 576956e31..e46ee5cf8 100644 --- a/src/target/lakemont.c +++ b/src/target/lakemont.c @@ -1070,7 +1070,8 @@ int lakemont_step(struct target *t, int current, LOG_DEBUG("EFLAGS [TF] [RF] bits set=0x%08" PRIx32 ", PMCR=0x%08" PRIx32 ", EIP=0x%08" PRIx32, eflags, pmcr, eip); - tapstatus = get_tapstatus(t); + /* Returned value unused. Can this line be removed? */ + get_tapstatus(t); t->debug_reason = DBG_REASON_SINGLESTEP; t->state = TARGET_DEBUG_RUNNING; From 8f1971295b251dc13b687db493c2daa7f922e940 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 22 Sep 2021 19:21:39 +0200 Subject: [PATCH 06/15] mips64_pracc: fix three dead assignments Clang scan-build complains for three dead assignments: Although the value stored to 'data' is used in the enclosing expression, the value is never actually read from 'data' Value stored to 'address' is never read Remove the useless assignment and the variable 'data'. Change-Id: Ie8dcb74b1c1aa5eea1acd06b3c45c5b44954c9e7 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6592 Tested-by: jenkins --- src/target/mips64_pracc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/target/mips64_pracc.c b/src/target/mips64_pracc.c index 9583ad767..bb2af228d 100644 --- a/src/target/mips64_pracc.c +++ b/src/target/mips64_pracc.c @@ -213,7 +213,7 @@ int mips64_pracc_exec(struct mips_ejtag *ejtag_info, unsigned num_param_out, uint64_t *param_out) { uint32_t ejtag_ctrl; - uint64_t address = 0, address_prev = 0, data; + uint64_t address = 0, address_prev = 0; struct mips64_pracc_context ctx; int retval; int pass = 0; @@ -243,7 +243,7 @@ int mips64_pracc_exec(struct mips_ejtag *ejtag_info, address_prev = address; else address_prev = 0; - address32 = data = 0; + address32 = 0; mips_ejtag_set_instr(ejtag_info, EJTAG_INST_ADDRESS); mips_ejtag_drscan_32(ejtag_info, &address32); @@ -1358,8 +1358,6 @@ int mips64_pracc_fastdata_xfer(struct mips_ejtag *ejtag_info, 0, NULL, 0, NULL); /* next fetch to dmseg should be in FASTDATA_AREA, check */ - address = 0; - mips_ejtag_set_instr(ejtag_info, EJTAG_INST_ADDRESS); retval = mips_ejtag_drscan_32(ejtag_info, &address32); if (retval != ERROR_OK) @@ -1411,7 +1409,6 @@ int mips64_pracc_fastdata_xfer(struct mips_ejtag *ejtag_info, return retval; } - address = 0; mips_ejtag_set_instr(ejtag_info, EJTAG_INST_ADDRESS); retval = mips_ejtag_drscan_32(ejtag_info, &address32); if (retval != ERROR_OK) { From 0184ddba186fa618775865792e1eda9e86d9bb91 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 22 Sep 2021 23:14:22 +0200 Subject: [PATCH 07/15] command: document enum command_mode Add the description to doxygen documentation. Change-Id: Iec04b4a37088e1b3b52ca84102820f450528b5b9 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6593 Tested-by: jenkins --- src/helper/command.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/helper/command.h b/src/helper/command.h index fb9e50c85..796cd9d3b 100644 --- a/src/helper/command.h +++ b/src/helper/command.h @@ -38,6 +38,15 @@ #define PRINTF_ATTRIBUTE_FORMAT printf #endif +/** + * OpenOCD command mode is COMMAND_CONFIG at start, then switches to COMMAND_EXEC + * during the execution of command 'init'. + * The field 'mode' in struct command_registration specifies in which command mode + * the command can be executed: + * - during COMMAND_CONFIG only, + * - during COMMAND_EXEC only, + * - in both modes (COMMAND_ANY). + */ enum command_mode { COMMAND_EXEC, COMMAND_CONFIG, From 8a522d96c79aed654fc7273c7c5c3f680c91c333 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Sat, 2 Oct 2021 14:22:06 +0200 Subject: [PATCH 08/15] flash/nor/psoc6: fix doxygen comment Commit 64c2e03b23d9 ("flash/nor: improved API of flash_driver.info & fixed buffer overruns") changes the prototype of the function psoc6_get_info() but didn't update the list of parameters in the doxygen comment. Fix the doxygen comment. Change-Id: I1dce018b60d080973c5e351490d4d7baba422d74 Signed-off-by: Antonio Borneo Fixes: 64c2e03b23d9 ("flash/nor: improved API of flash_driver.info & fixed buffer overruns") Reviewed-on: https://review.openocd.org/c/openocd/+/6620 Reviewed-by: Tomas Vanek Tested-by: jenkins --- src/flash/nor/psoc6.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/flash/nor/psoc6.c b/src/flash/nor/psoc6.c index 8a4121950..d3a4b3702 100644 --- a/src/flash/nor/psoc6.c +++ b/src/flash/nor/psoc6.c @@ -492,8 +492,7 @@ static const char *protection_to_str(uint8_t protection) /** *********************************************************************************************** * @brief psoc6_get_info Displays human-readable information about acquired device * @param bank current flash bank - * @param buf pointer to buffer for human-readable text - * @param buf_size size of the buffer + * @param cmd pointer to command invocation instance * @return ERROR_OK in case of success, ERROR_XXX code otherwise *************************************************************************************************/ static int psoc6_get_info(struct flash_bank *bank, struct command_invocation *cmd) From f4612e06c61f6c46cff936d2c6b48d6f2627ff61 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 7 Apr 2021 19:21:27 +0200 Subject: [PATCH 09/15] tcl/stm32mp15x: freeze watchdog, recover SWD after power cycle Freeze the IWDG watchdog when cores are halted to prevent a reset while debugging. The PMIC present on some board senses the nsrst and forces a power cycle to the target. The power cycle causes the SWJ-DP to restart in JTAG mode. If the debugger is using SWD, the mismatch triggers an error after the reset command. Ignore the error detected by 'dap init' and proceed executing the handler. The error in 'dap init' will force a reconnect during the following 'dap apid', restoring the SWD functionality. Change-Id: I04fcda6a5b8a1b080ab4e8890ecd0754d5ed12d9 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6599 Tested-by: jenkins --- tcl/target/stm32mp15x.cfg | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tcl/target/stm32mp15x.cfg b/tcl/target/stm32mp15x.cfg index e50ef9c20..639fbabe0 100644 --- a/tcl/target/stm32mp15x.cfg +++ b/tcl/target/stm32mp15x.cfg @@ -97,6 +97,9 @@ axi_secure proc dbgmcu_enable_debug {} { # set debug enable bits in DBGMCU_CR to get ap2 and cm4 visible catch {$::_CHIPNAME.ap1 mww 0xe0081004 0x00000007} + # freeze watchdog 1 and 2 on cores halted + catch {$::_CHIPNAME.ap1 mww 0xe008102c 0x00000004} + catch {$::_CHIPNAME.ap1 mww 0xe008104c 0x00000008} } proc toggle_cpu0_dbg_claim0 {} { @@ -116,7 +119,7 @@ proc rcc_enable_traceclk {} { } # FIXME: most of handler below will be removed once reset framework get merged -$_CHIPNAME.ap1 configure -event reset-deassert-pre {adapter deassert srst deassert trst;dap init;catch {$::_CHIPNAME.dap apid 1}} +$_CHIPNAME.ap1 configure -event reset-deassert-pre {adapter deassert srst deassert trst;catch {dap init};catch {$::_CHIPNAME.dap apid 1}} $_CHIPNAME.ap2 configure -event reset-deassert-pre {dbgmcu_enable_debug;rcc_enable_traceclk} $_CHIPNAME.cpu0 configure -event reset-deassert-pre {$::_CHIPNAME.cpu0 arp_examine} $_CHIPNAME.cpu1 configure -event reset-deassert-pre {$::_CHIPNAME.cpu1 arp_examine allow-defer} From 615709d14049027e6172fbb7f6cf6c898eefaea9 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Wed, 1 Sep 2021 15:00:46 -0700 Subject: [PATCH 10/15] Upstream a whole host of RISC-V changes. Made no attempt to separate this out into reviewable chunks, since this is all RISC-V-specific code developed at https://github.com/riscv/riscv-openocd Memory sample and repeat read functionality was left out of this change since it requires some target-independent changes that I'll upstream some other time. Change-Id: I92917c86d549c232cbf36ffbfefc93331c05accd Signed-off-by: Tim Newsome Reviewed-on: https://review.openocd.org/c/openocd/+/6529 Tested-by: jenkins Reviewed-by: Antonio Borneo --- src/target/riscv/batch.c | 26 +- src/target/riscv/debug_defines.h | 860 ++++++++++++-- src/target/riscv/opcodes.h | 187 ++-- src/target/riscv/program.h | 10 - src/target/riscv/riscv-011.c | 110 +- src/target/riscv/riscv-013.c | 1542 +++++++++++++------------- src/target/riscv/riscv.c | 1399 +++++++++++++---------- src/target/riscv/riscv.h | 119 +- src/target/riscv/riscv_semihosting.c | 19 +- 9 files changed, 2617 insertions(+), 1655 deletions(-) diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c index 3c062e150..a3d68a91f 100644 --- a/src/target/riscv/batch.c +++ b/src/target/riscv/batch.c @@ -27,23 +27,33 @@ struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_ out->allocated_scans = scans; out->idle_count = idle; out->data_out = malloc(sizeof(*out->data_out) * (scans) * DMI_SCAN_BUF_SIZE); - if (!out->data_out) + if (!out->data_out) { + LOG_ERROR("Failed to allocate data_out in RISC-V batch."); goto error1; + }; out->data_in = malloc(sizeof(*out->data_in) * (scans) * DMI_SCAN_BUF_SIZE); - if (!out->data_in) + if (!out->data_in) { + LOG_ERROR("Failed to allocate data_in in RISC-V batch."); goto error2; + } out->fields = malloc(sizeof(*out->fields) * (scans)); - if (!out->fields) + if (!out->fields) { + LOG_ERROR("Failed to allocate fields in RISC-V batch."); goto error3; + } if (bscan_tunnel_ir_width != 0) { out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * (scans)); - if (!out->bscan_ctxt) + if (!out->bscan_ctxt) { + LOG_ERROR("Failed to allocate bscan_ctxt in RISC-V batch."); goto error4; + } } out->last_scan = RISCV_SCAN_TYPE_INVALID; out->read_keys = malloc(sizeof(*out->read_keys) * (scans)); - if (!out->read_keys) + if (!out->read_keys) { + LOG_ERROR("Failed to allocate read_keys in RISC-V batch."); goto error5; + } return out; error5: @@ -82,8 +92,6 @@ int riscv_batch_run(struct riscv_batch *batch) return ERROR_OK; } - keep_alive(); - riscv_batch_add_nop(batch); for (size_t i = 0; i < batch->used_scans; ++i) { @@ -96,11 +104,15 @@ int riscv_batch_run(struct riscv_batch *batch) jtag_add_runtest(batch->idle_count, TAP_IDLE); } + keep_alive(); + if (jtag_execute_queue() != ERROR_OK) { LOG_ERROR("Unable to execute JTAG queue"); return ERROR_FAIL; } + keep_alive(); + 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) diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h index cb518a891..b5104d530 100644 --- a/src/target/riscv/debug_defines.h +++ b/src/target/riscv/debug_defines.h @@ -1,6 +1,7 @@ /* * This file is auto-generated by running 'make debug_defines.h' in - * https://github.com/riscv/riscv-debug-spec/ (30b1a97) + * https://github.com/riscv/riscv-debug-spec/ (63c985f) + * License: Creative Commons Attribution 4.0 International Public License (CC BY 4.0) */ #define DTM_IDCODE 0x01 @@ -89,7 +90,7 @@ /* * 0: Version described in spec version 0.11. * - * 1: Version described in spec version 0.13. + * 1: Version described in spec versions 0.13 and 1.0. * * 15: Version not described in any available version of this spec. */ @@ -155,16 +156,38 @@ #define DTM_DMI_OP (0x3ULL << DTM_DMI_OP_OFFSET) #define CSR_DCSR 0x7b0 /* - * 0: There is no external debug support. + * 0: There is no debug support. * - * 4: External debug support exists as it is described in this document. + * 4: Debug support exists as it is described in this document. * - * 15: There is external debug support, but it does not conform to any + * 15: There is debug support, but it does not conform to any * available version of this spec. */ -#define CSR_DCSR_XDEBUGVER_OFFSET 28 -#define CSR_DCSR_XDEBUGVER_LENGTH 4 -#define CSR_DCSR_XDEBUGVER (0xfU << CSR_DCSR_XDEBUGVER_OFFSET) +#define CSR_DCSR_DEBUGVER_OFFSET 28 +#define CSR_DCSR_DEBUGVER_LENGTH 4 +#define CSR_DCSR_DEBUGVER (0xfU << CSR_DCSR_DEBUGVER_OFFSET) +/* + * 0: {\tt ebreak} instructions in VS-mode behave as described in the + * Privileged Spec. + * + * 1: {\tt ebreak} instructions in VS-mode enter Debug Mode. + * + * This bit is hardwired to 0 if the hart does not support virtualization mode. + */ +#define CSR_DCSR_EBREAKVS_OFFSET 17 +#define CSR_DCSR_EBREAKVS_LENGTH 1 +#define CSR_DCSR_EBREAKVS (0x1U << CSR_DCSR_EBREAKVS_OFFSET) +/* + * 0: {\tt ebreak} instructions in VU-mode behave as described in the + * Privileged Spec. + * + * 1: {\tt ebreak} instructions in VU-mode enter Debug Mode. + * + * This bit is hardwired to 0 if the hart does not support virtualization mode. + */ +#define CSR_DCSR_EBREAKVU_OFFSET 16 +#define CSR_DCSR_EBREAKVU_LENGTH 1 +#define CSR_DCSR_EBREAKVU (0x1U << CSR_DCSR_EBREAKVU_OFFSET) /* * 0: {\tt ebreak} instructions in M-mode behave as described in the * Privileged Spec. @@ -180,7 +203,7 @@ * * 1: {\tt ebreak} instructions in S-mode enter Debug Mode. * - * This bit is hardwired to 0 if the hart does not support S mode. + * This bit is hardwired to 0 if the hart does not support S-mode. */ #define CSR_DCSR_EBREAKS_OFFSET 13 #define CSR_DCSR_EBREAKS_LENGTH 1 @@ -191,7 +214,7 @@ * * 1: {\tt ebreak} instructions in U-mode enter Debug Mode. * - * This bit is hardwired to 0 if the hart does not support U mode. + * This bit is hardwired to 0 if the hart does not support U-mode. */ #define CSR_DCSR_EBREAKU_OFFSET 12 #define CSR_DCSR_EBREAKU_LENGTH 1 @@ -262,9 +285,20 @@ #define CSR_DCSR_CAUSE_LENGTH 3 #define CSR_DCSR_CAUSE (0x7U << CSR_DCSR_CAUSE_OFFSET) /* - * 0: \FcsrMcontrolMprv in \Rmstatus is ignored in Debug Mode. + * Extends the prv field with the virtualization mode the hart was operating + * in when Debug Mode was entered. The encoding is described in Table + * \ref{tab:privlevel}. + * A debugger can change this value to change the hart's virtualization mode + * when exiting Debug Mode. + * This bit is hardwired to 0 on harts that do not support virtualization mode. + */ +#define CSR_DCSR_V_OFFSET 5 +#define CSR_DCSR_V_LENGTH 1 +#define CSR_DCSR_V (0x1U << CSR_DCSR_V_OFFSET) +/* + * 0: \FcsrMstatusMprv in \Rmstatus is ignored in Debug Mode. * - * 1: \FcsrMcontrolMprv in \Rmstatus takes effect in Debug Mode. + * 1: \FcsrMstatusMprv in \Rmstatus takes effect in Debug Mode. * * Implementing this bit is optional. It may be tied to either 0 or 1. */ @@ -334,6 +368,14 @@ * 5: The trigger is an exception trigger. The remaining bits * in this register act as described in \RcsrEtrigger. * + * 6: The trigger is an address/data match trigger. The remaining bits + * in this register act as described in \RcsrMcontrolSix. This is similar + * to a type 2 trigger, but provides additional functionality and + * should be used instead of type 2 in newer implementations. + * + * 7: The trigger is a trigger source external to the TM. The + * remaining bits in this register act as described in \RcsrTmexttrigger. + * * 12--14: These trigger types are available for non-standard use. * * 15: This trigger exists (so enumeration shouldn't terminate), but @@ -354,7 +396,9 @@ * selected \RcsrTselect. Writes from other modes are ignored. * * This bit is only writable from Debug Mode. - * When clearing this bit, the debugger should also clear the action field + * In ordinary use, external debuggers will always set this bit when + * configuring a trigger. + * When clearing this bit, debuggers should also clear the action field * (whose location depends on \FcsrTdataOneType). */ #define CSR_TDATA1_DMODE_OFFSET (XLEN-5) @@ -389,9 +433,36 @@ #define CSR_TINFO_INFO_LENGTH 16 #define CSR_TINFO_INFO (0xffffULL << CSR_TINFO_INFO_OFFSET) #define CSR_TCONTROL 0x7a5 +/* + * \RcsrHcontext enable. + * + * 0: \RcsrHcontext is set to 0 and writes are ignored. + * + * 1: \RcsrHcontext may be written and read. + */ +#define CSR_TCONTROL_HCXE_OFFSET 9 +#define CSR_TCONTROL_HCXE_LENGTH 1 +#define CSR_TCONTROL_HCXE (0x1ULL << CSR_TCONTROL_HCXE_OFFSET) +/* + * \RcsrScontext enable. + * + * 0: \RcsrScontext is set to 0 and writes are ignored. + * + * 1: \RcsrScontext may be written and read. + * + * Enabling \RcsrScontext can be a security risk in a + * virtualized system with a hypervisor that does not swap \RcsrScontext. + */ +#define CSR_TCONTROL_SCXE_OFFSET 8 +#define CSR_TCONTROL_SCXE_LENGTH 1 +#define CSR_TCONTROL_SCXE (0x1ULL << CSR_TCONTROL_SCXE_OFFSET) /* * M-mode previous trigger enable field. * + * \FcsrTcontrolMpte and \FcsrTcontrolMte provide one solution to a problem + * regarding triggers with action=0 firing in M-mode trap handlers. See + * Section~\ref{sec:mmtrigger} for more details. + * * When a trap into M-mode is taken, \FcsrTcontrolMpte is set to the value of * \FcsrTcontrolMte. */ @@ -411,20 +482,23 @@ #define CSR_TCONTROL_MTE_OFFSET 3 #define CSR_TCONTROL_MTE_LENGTH 1 #define CSR_TCONTROL_MTE (0x1ULL << CSR_TCONTROL_MTE_OFFSET) -#define CSR_MCONTEXT 0x7a8 +#define CSR_HCONTEXT 0x6a8 /* - * Machine mode software can write a context number to this register, + * Hypervisor mode software can write a context number to this register, * which can be used to set triggers that only fire in that specific * context. * * An implementation may tie any number of upper bits in this field to - * 0. It's recommended to implement no more than 6 bits on RV32, and - * 13 on RV64. + * 0. If the H extension is not implemented, it's recommended to implement + * no more than 6 bits on RV32 and 13 on RV64 (as visible through the + * \RcsrMcontext register). If the H extension is implemented, + * it's recommended to implement no more than 7 bits on RV32 + * and 14 on RV64. */ -#define CSR_MCONTEXT_MCONTEXT_OFFSET 0 -#define CSR_MCONTEXT_MCONTEXT_LENGTH XLEN -#define CSR_MCONTEXT_MCONTEXT (((1L << XLEN) - 1) << CSR_MCONTEXT_MCONTEXT_OFFSET) -#define CSR_SCONTEXT 0x7aa +#define CSR_HCONTEXT_HCONTEXT_OFFSET 0 +#define CSR_HCONTEXT_HCONTEXT_LENGTH XLEN +#define CSR_HCONTEXT_HCONTEXT (((1L << XLEN) - 1) << CSR_HCONTEXT_HCONTEXT_OFFSET) +#define CSR_SCONTEXT 0x5a8 /* * Supervisor mode software can write a context number to this * register, which can be used to set triggers that only fire in that @@ -437,6 +511,8 @@ #define CSR_SCONTEXT_DATA_OFFSET 0 #define CSR_SCONTEXT_DATA_LENGTH XLEN #define CSR_SCONTEXT_DATA (((1L << XLEN) - 1) << CSR_SCONTEXT_DATA_OFFSET) +#define CSR_MCONTEXT 0x7a8 +#define CSR_MSCONTEXT 0x7aa #define CSR_MCONTROL 0x7a1 #define CSR_MCONTROL_TYPE_OFFSET (XLEN-4) #define CSR_MCONTROL_TYPE_LENGTH 4 @@ -447,11 +523,10 @@ /* * Specifies the largest naturally aligned powers-of-two (NAPOT) range * supported by the hardware when \FcsrMcontrolMatch is 1. The value is the - * logarithm base 2 of the - * number of bytes in that range. A value of 0 indicates that only - * exact value matches are supported (one byte range). A value of 63 - * corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in - * size. + * logarithm base 2 of the number of bytes in that range. + * A value of 0 indicates \FcsrMcontrolMatch 1 is not supported. + * A value of 63 corresponds to the maximum NAPOT range, which is + * $2^{63}$ bytes in size. */ #define CSR_MCONTROL_MASKMAX_OFFSET (XLEN-11) #define CSR_MCONTROL_MASKMAX_LENGTH 6 @@ -466,8 +541,9 @@ #define CSR_MCONTROL_SIZEHI_LENGTH 2 #define CSR_MCONTROL_SIZEHI (0x3ULL << CSR_MCONTROL_SIZEHI_OFFSET) /* - * If this bit is implemented, the hardware sets it when this - * trigger matches. The trigger's user can set or clear it at any + * If this bit is implemented then it must become set when this + * trigger fires and may become set when this trigger matches. + * The trigger's user can set or clear it at any * time. It is used to determine which * trigger(s) matched. If the bit is not implemented, it is always 0 * and writing it has no effect. @@ -476,36 +552,41 @@ #define CSR_MCONTROL_HIT_LENGTH 1 #define CSR_MCONTROL_HIT (0x1ULL << CSR_MCONTROL_HIT_OFFSET) /* - * 0: Perform a match on the lowest virtual address of the access. In - * addition, it is recommended that the trigger also fires if any of - * the other accessed virtual addresses match. + * This bit determines the contents of the XLEN-bit compare values. + * + * 0: There is at least one compare value and it contains the lowest + * virtual address of the access. + * It is recommended that there are additional compare values for + * the other accessed virtual addresses. * (E.g. on a 32-bit read from 0x4000, the lowest address is 0x4000 * and the other addresses are 0x4001, 0x4002, and 0x4003.) * - * 1: Perform a match on the data value loaded or stored, or the - * instruction executed. + * 1: There is exactly one compare value and it contains the data + * value loaded or stored, or the instruction executed. + * Any bits beyond the size of the data access will contain 0. */ #define CSR_MCONTROL_SELECT_OFFSET 19 #define CSR_MCONTROL_SELECT_LENGTH 1 #define CSR_MCONTROL_SELECT (0x1ULL << CSR_MCONTROL_SELECT_OFFSET) /* * 0: The action for this trigger will be taken just before the - * instruction that triggered it is executed, but after all preceding - * instructions are committed. \Rmepc or \RcsrDpc (depending on - * \FcsrMcontrolAction) must be set to the virtual address of the + * instruction that triggered it is committed, but after all preceding + * instructions are committed. \Rxepc or \RcsrDpc (depending + * on \FcsrMcontrolAction) must be set to the virtual address of the * instruction that matched. * - * If this is combined with \FcsrMcontrolLoad then a memory access will be + * If this is combined with \FcsrMcontrolLoad and + * \FcsrMcontrolSelect=1 then a memory access will be * performed (including any side effects of performing such an access) even * though the load will not update its destination register. Debuggers * should consider this when setting such breakpoints on, for example, * memory-mapped I/O addresses. * * 1: The action for this trigger will be taken after the instruction - * that triggered it is executed. It should be taken before the next - * instruction is executed, but it is better to implement triggers imprecisely - * than to not implement them at all. - * \Rmepc or \RcsrDpc (depending on \FcsrMcontrolAction) must be set to + * that triggered it is committed. It should be taken before the next + * instruction is committed, but it is better to implement triggers imprecisely + * than to not implement them at all. \Rxepc or + * \RcsrDpc (depending on \FcsrMcontrolAction) must be set to * the virtual address of the next instruction that must be executed to * preserve the program flow. * @@ -559,9 +640,21 @@ * execution of 128-bit instructions. * * An implementation must support the value of 0, but all other values - * are optional. It is recommended to support triggers for every - * access size the hart supports, as well as for every instruction - * size the hart supports. + * are optional. When an implementation supports address triggers + * (\FcsrMcontrolSelect=0), it is recommended that those triggers + * support every access size that the hart supports, as well as for + * every instruction size that the hart supports. + * + * Implementations such as RV32D or RV64V are able to perform loads + * and stores that are wider than XLEN. Custom extensions may also + * support instructions that are wider than XLEN. Because + * \RcsrTdataTwo is of size XLEN, there is a known limitation that + * data value triggers (\FcsrMcontrolSelect=1) can only be supported + * for access sizes up to XLEN bits. When an implementation supports + * data value triggers (\FcsrMcontrolSelect=1), it is recommended + * that those triggers support every access size up to XLEN that the + * hart supports, as well as for every instruction length up to XLEN + * that the hart supports. */ #define CSR_MCONTROL_SIZELO_OFFSET 16 #define CSR_MCONTROL_SIZELO_LENGTH 2 @@ -587,6 +680,9 @@ * trigger will be taken if and only if all the triggers in the chain * match at the same time. * + * Debuggers should not terminate a chain with a trigger with a + * different type. It is undefined when exactly such a chain fires. + * * Because \FcsrMcontrolChain affects the next trigger, hardware must zero it in * writes to \RcsrMcontrol that set \FcsrTdataOneDmode to 0 if the next trigger has * \FcsrTdataOneDmode of 1. @@ -603,26 +699,31 @@ #define CSR_MCONTROL_CHAIN_LENGTH 1 #define CSR_MCONTROL_CHAIN (0x1ULL << CSR_MCONTROL_CHAIN_OFFSET) /* - * 0: Matches when the value equals \RcsrTdataTwo. + * 0: Matches when any compare value equals \RcsrTdataTwo. * - * 1: Matches when the top M bits of the value match the top M bits of - * \RcsrTdataTwo. M is XLEN-1 minus the index of the least-significant + * 1: Matches when the top $M$ bits of any compare value match the top + * $M$ bits of \RcsrTdataTwo. + * $M$ is $|XLEN|-1$ minus the index of the least-significant * bit containing 0 in \RcsrTdataTwo. Debuggers should only write values - * to \RcsrTdataTwo such that M + \FcsrMcontrolMaskmax $\geq$ XLEN, otherwise it's - * undefined on what conditions the trigger will fire. + * to \RcsrTdataTwo such that $M + $\FcsrMcontrolMaskmax$ \geq |XLEN|$ + * and $M\gt0$ , otherwise it's undefined on what conditions the + * trigger will match. * - * 2: Matches when the value is greater than (unsigned) or equal to + * 2: Matches when any compare value is greater than (unsigned) or + * equal to \RcsrTdataTwo. + * + * 3: Matches when any compare value is less than (unsigned) * \RcsrTdataTwo. * - * 3: Matches when the value is less than (unsigned) \RcsrTdataTwo. + * 4: Matches when $\frac{|XLEN|}{2}-1$:$0$ of any compare value + * equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after + * $\frac{|XLEN|}{2}-1$:$0$ of the compare value is ANDed with + * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo. * - * 4: Matches when the lower half of the value equals the lower half - * of \RcsrTdataTwo after the lower half of the value is ANDed with the - * upper half of \RcsrTdataTwo. - * - * 5: Matches when the upper half of the value equals the lower half - * of \RcsrTdataTwo after the upper half of the value is ANDed with the - * upper half of \RcsrTdataTwo. + * 5: Matches when $|XLEN|-1$:$\frac{|XLEN|}{2}$ of any compare + * value equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after + * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of the compare value is ANDed with + * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo. * * 8: Matches when \FcsrMcontrolMatch$=0$ would not match. * @@ -633,6 +734,12 @@ * 13: Matches when \FcsrMcontrolMatch$=5$ would not match. * * Other values are reserved for future use. + * + * All comparisons only look at the lower XLEN (in the current mode) + * bits of the compare values and of \RcsrTdataTwo. + * When \FcsrMcontrolSelect=1 and access size is N, this is further + * reduced, and comparisons only look at the lower N bits of the + * compare values and of \RcsrTdataTwo. */ #define CSR_MCONTROL_MATCH_OFFSET 7 #define CSR_MCONTROL_MATCH_LENGTH 4 @@ -644,13 +751,17 @@ #define CSR_MCONTROL_M_LENGTH 1 #define CSR_MCONTROL_M (0x1ULL << CSR_MCONTROL_M_OFFSET) /* - * When set, enable this trigger in S-mode. + * When set, enable this trigger in S/HS-mode. + * This bit is hard-wired to 0 if the hart does not support + * S-mode. */ #define CSR_MCONTROL_S_OFFSET 4 #define CSR_MCONTROL_S_LENGTH 1 #define CSR_MCONTROL_S (0x1ULL << CSR_MCONTROL_S_OFFSET) /* * When set, enable this trigger in U-mode. + * This bit is hard-wired to 0 if the hart does not support + * U-mode. */ #define CSR_MCONTROL_U_OFFSET 3 #define CSR_MCONTROL_U_LENGTH 1 @@ -676,6 +787,273 @@ #define CSR_MCONTROL_LOAD_OFFSET 0 #define CSR_MCONTROL_LOAD_LENGTH 1 #define CSR_MCONTROL_LOAD (0x1ULL << CSR_MCONTROL_LOAD_OFFSET) +#define CSR_MCONTROL6 0x7a1 +#define CSR_MCONTROL6_TYPE_OFFSET (XLEN-4) +#define CSR_MCONTROL6_TYPE_LENGTH 4 +#define CSR_MCONTROL6_TYPE (0xfULL << CSR_MCONTROL6_TYPE_OFFSET) +#define CSR_MCONTROL6_DMODE_OFFSET (XLEN-5) +#define CSR_MCONTROL6_DMODE_LENGTH 1 +#define CSR_MCONTROL6_DMODE (0x1ULL << CSR_MCONTROL6_DMODE_OFFSET) +/* + * When set, enable this trigger in VS-mode. + * This bit is hard-wired to 0 if the hart does not support + * virtualization mode. + */ +#define CSR_MCONTROL6_VS_OFFSET 24 +#define CSR_MCONTROL6_VS_LENGTH 1 +#define CSR_MCONTROL6_VS (0x1ULL << CSR_MCONTROL6_VS_OFFSET) +/* + * When set, enable this trigger in VU-mode. + * This bit is hard-wired to 0 if the hart does not support + * virtualization mode. + */ +#define CSR_MCONTROL6_VU_OFFSET 23 +#define CSR_MCONTROL6_VU_LENGTH 1 +#define CSR_MCONTROL6_VU (0x1ULL << CSR_MCONTROL6_VU_OFFSET) +/* + * If this bit is implemented, the hardware sets it when this + * trigger matches. The trigger's user can set or clear it at any + * time. It is used to determine which + * trigger(s) matched. If the bit is not implemented, it is always 0 + * and writing it has no effect. + */ +#define CSR_MCONTROL6_HIT_OFFSET 22 +#define CSR_MCONTROL6_HIT_LENGTH 1 +#define CSR_MCONTROL6_HIT (0x1ULL << CSR_MCONTROL6_HIT_OFFSET) +/* + * This bit determines the contents of the XLEN-bit compare values. + * + * 0: There is at least one compare value and it contains the lowest + * virtual address of the access. + * In addition, it is recommended that there are additional compare + * values for the other accessed virtual addresses match. + * (E.g. on a 32-bit read from 0x4000, the lowest address is 0x4000 + * and the other addresses are 0x4001, 0x4002, and 0x4003.) + * + * 1: There is exactly one compare value and it contains the data + * value loaded or stored, or the instruction executed. + * Any bits beyond the size of the data access will contain 0. + */ +#define CSR_MCONTROL6_SELECT_OFFSET 21 +#define CSR_MCONTROL6_SELECT_LENGTH 1 +#define CSR_MCONTROL6_SELECT (0x1ULL << CSR_MCONTROL6_SELECT_OFFSET) +/* + * 0: The action for this trigger will be taken just before the + * instruction that triggered it is committed, but after all preceding + * instructions are committed. \Rxepc or \RcsrDpc (depending + * on \FcsrMcontrolSixAction) must be set to the virtual address of the + * instruction that matched. + * + * If this is combined with \FcsrMcontrolSixLoad and + * \FcsrMcontrolSixSelect=1 then a memory access will be + * performed (including any side effects of performing such an access) even + * though the load will not update its destination register. Debuggers + * should consider this when setting such breakpoints on, for example, + * memory-mapped I/O addresses. + * + * 1: The action for this trigger will be taken after the instruction + * that triggered it is committed. It should be taken before the next + * instruction is committed, but it is better to implement triggers imprecisely + * than to not implement them at all. \Rxepc or + * \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set to + * the virtual address of the next instruction that must be executed to + * preserve the program flow. + * + * Most hardware will only implement one timing or the other, possibly + * dependent on \FcsrMcontrolSixSelect, \FcsrMcontrolSixExecute, + * \FcsrMcontrolSixLoad, and \FcsrMcontrolSixStore. This bit + * primarily exists for the hardware to communicate to the debugger + * what will happen. Hardware may implement the bit fully writable, in + * which case the debugger has a little more control. + * + * Data load triggers with \FcsrMcontrolSixTiming of 0 will result in the same load + * happening again when the debugger lets the hart run. For data load + * triggers, debuggers must first attempt to set the breakpoint with + * \FcsrMcontrolSixTiming of 1. + * + * If a trigger with \FcsrMcontrolSixTiming of 0 matches, it is + * implementation-dependent whether that prevents a trigger with + * \FcsrMcontrolSixTiming of 1 matching as well. + */ +#define CSR_MCONTROL6_TIMING_OFFSET 20 +#define CSR_MCONTROL6_TIMING_LENGTH 1 +#define CSR_MCONTROL6_TIMING (0x1ULL << CSR_MCONTROL6_TIMING_OFFSET) +/* + * 0: The trigger will attempt to match against an access of any size. + * The behavior is only well-defined if $|select|=0$, or if the access + * size is XLEN. + * + * 1: The trigger will only match against 8-bit memory accesses. + * + * 2: The trigger will only match against 16-bit memory accesses or + * execution of 16-bit instructions. + * + * 3: The trigger will only match against 32-bit memory accesses or + * execution of 32-bit instructions. + * + * 4: The trigger will only match against execution of 48-bit instructions. + * + * 5: The trigger will only match against 64-bit memory accesses or + * execution of 64-bit instructions. + * + * 6: The trigger will only match against execution of 80-bit instructions. + * + * 7: The trigger will only match against execution of 96-bit instructions. + * + * 8: The trigger will only match against execution of 112-bit instructions. + * + * 9: The trigger will only match against 128-bit memory accesses or + * execution of 128-bit instructions. + * + * An implementation must support the value of 0, but all other values + * are optional. When an implementation supports address triggers + * (\FcsrMcontrolSixSelect=0), it is recommended that those triggers + * support every access size that the hart supports, as well as for + * every instruction size that the hart supports. + * + * Implementations such as RV32D or RV64V are able to perform loads + * and stores that are wider than XLEN. Custom extensions may also + * support instructions that are wider than XLEN. Because + * \RcsrTdataTwo is of size XLEN, there is a known limitation that + * data value triggers (\FcsrMcontrolSixSelect=1) can only be supported + * for access sizes up to XLEN bits. When an implementation supports + * data value triggers (\FcsrMcontrolSixSelect=1), it is recommended + * that those triggers support every access size up to XLEN that the + * hart supports, as well as for every instruction length up to XLEN + * that the hart supports. + */ +#define CSR_MCONTROL6_SIZE_OFFSET 16 +#define CSR_MCONTROL6_SIZE_LENGTH 4 +#define CSR_MCONTROL6_SIZE (0xfULL << CSR_MCONTROL6_SIZE_OFFSET) +/* + * The action to take when the trigger fires. The values are explained + * in Table~\ref{tab:action}. + */ +#define CSR_MCONTROL6_ACTION_OFFSET 12 +#define CSR_MCONTROL6_ACTION_LENGTH 4 +#define CSR_MCONTROL6_ACTION (0xfULL << CSR_MCONTROL6_ACTION_OFFSET) +/* + * 0: When this trigger matches, the configured action is taken. + * + * 1: While this trigger does not match, it prevents the trigger with + * the next index from matching. + * + * A trigger chain starts on the first trigger with $|chain|=1$ after + * a trigger with $|chain|=0$, or simply on the first trigger if that + * has $|chain|=1$. It ends on the first trigger after that which has + * $|chain|=0$. This final trigger is part of the chain. The action + * on all but the final trigger is ignored. The action on that final + * trigger will be taken if and only if all the triggers in the chain + * match at the same time. + * + * Debuggers should not terminate a chain with a trigger with a + * different type. It is undefined when exactly such a chain fires. + * + * Because \FcsrMcontrolSixChain affects the next trigger, hardware must zero it in + * writes to \RcsrMcontrolSix that set \FcsrTdataOneDmode to 0 if the next trigger has + * \FcsrTdataOneDmode of 1. + * In addition hardware should ignore writes to \RcsrMcontrolSix that set + * \FcsrTdataOneDmode to 1 if the previous trigger has both \FcsrTdataOneDmode of 0 and + * \FcsrMcontrolSixChain of 1. Debuggers must avoid the latter case by checking + * \FcsrMcontrolSixChain on the previous trigger if they're writing \RcsrMcontrolSix. + * + * Implementations that wish to limit the maximum length of a trigger + * chain (eg. to meet timing requirements) may do so by zeroing + * \FcsrMcontrolSixChain in writes to \RcsrMcontrolSix that would make the chain too long. + */ +#define CSR_MCONTROL6_CHAIN_OFFSET 11 +#define CSR_MCONTROL6_CHAIN_LENGTH 1 +#define CSR_MCONTROL6_CHAIN (0x1ULL << CSR_MCONTROL6_CHAIN_OFFSET) +/* + * 0: Matches when any compare value equals \RcsrTdataTwo. + * + * 1: Matches when the top $M$ bits of any compare value match the top + * $M$ bits of \RcsrTdataTwo. + * $M$ is $|XLEN|-1$ minus the index of the least-significant bit + * containing 0 in \RcsrTdataTwo. + * \RcsrTdataTwo is WARL and bit $|maskmax6|-1$ will be set to 0 if no + * less significant bits are written with 0. + * Legal values for \RcsrTdataTwo require $M + |maskmax6| \geq |XLEN|$ and $M\gt0$. + * See above for how to determine maskmax6. + * + * 2: Matches when any compare value is greater than (unsigned) or + * equal to \RcsrTdataTwo. + * + * 3: Matches when any compare value is less than (unsigned) + * \RcsrTdataTwo. + * + * 4: Matches when $\frac{|XLEN|}{2}-1$:$0$ of any compare value + * equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after + * $\frac{|XLEN|}{2}-1$:$0$ of the compare value is ANDed with + * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo. + * + * 5: Matches when $|XLEN|-1$:$\frac{|XLEN|}{2}$ of any compare + * value equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after + * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of the compare value is ANDed with + * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo. + * + * 8: Matches when \FcsrMcontrolSixMatch$=0$ would not match. + * + * 9: Matches when \FcsrMcontrolSixMatch$=1$ would not match. + * + * 12: Matches when \FcsrMcontrolSixMatch$=4$ would not match. + * + * 13: Matches when \FcsrMcontrolSixMatch$=5$ would not match. + * + * Other values are reserved for future use. + * + * All comparisons only look at the lower XLEN (in the current mode) + * bits of the compare values and of \RcsrTdataTwo. + * When \FcsrMcontrolSelect=1 and access size is N, this is further + * reduced, and comparisons only look at the lower N bits of the + * compare values and of \RcsrTdataTwo. + */ +#define CSR_MCONTROL6_MATCH_OFFSET 7 +#define CSR_MCONTROL6_MATCH_LENGTH 4 +#define CSR_MCONTROL6_MATCH (0xfULL << CSR_MCONTROL6_MATCH_OFFSET) +/* + * When set, enable this trigger in M-mode. + */ +#define CSR_MCONTROL6_M_OFFSET 6 +#define CSR_MCONTROL6_M_LENGTH 1 +#define CSR_MCONTROL6_M (0x1ULL << CSR_MCONTROL6_M_OFFSET) +/* + * When set, enable this trigger in S/HS-mode. + * This bit is hard-wired to 0 if the hart does not support + * S-mode. + */ +#define CSR_MCONTROL6_S_OFFSET 4 +#define CSR_MCONTROL6_S_LENGTH 1 +#define CSR_MCONTROL6_S (0x1ULL << CSR_MCONTROL6_S_OFFSET) +/* + * When set, enable this trigger in U-mode. + * This bit is hard-wired to 0 if the hart does not support + * U-mode. + */ +#define CSR_MCONTROL6_U_OFFSET 3 +#define CSR_MCONTROL6_U_LENGTH 1 +#define CSR_MCONTROL6_U (0x1ULL << CSR_MCONTROL6_U_OFFSET) +/* + * When set, the trigger fires on the virtual address or opcode of an + * instruction that is executed. + */ +#define CSR_MCONTROL6_EXECUTE_OFFSET 2 +#define CSR_MCONTROL6_EXECUTE_LENGTH 1 +#define CSR_MCONTROL6_EXECUTE (0x1ULL << CSR_MCONTROL6_EXECUTE_OFFSET) +/* + * When set, the trigger fires on the virtual address or data of any + * store. + */ +#define CSR_MCONTROL6_STORE_OFFSET 1 +#define CSR_MCONTROL6_STORE_LENGTH 1 +#define CSR_MCONTROL6_STORE (0x1ULL << CSR_MCONTROL6_STORE_OFFSET) +/* + * When set, the trigger fires on the virtual address or data of any + * load. + */ +#define CSR_MCONTROL6_LOAD_OFFSET 0 +#define CSR_MCONTROL6_LOAD_LENGTH 1 +#define CSR_MCONTROL6_LOAD (0x1ULL << CSR_MCONTROL6_LOAD_OFFSET) #define CSR_ICOUNT 0x7a1 #define CSR_ICOUNT_TYPE_OFFSET (XLEN-4) #define CSR_ICOUNT_TYPE_LENGTH 4 @@ -683,6 +1061,22 @@ #define CSR_ICOUNT_DMODE_OFFSET (XLEN-5) #define CSR_ICOUNT_DMODE_LENGTH 1 #define CSR_ICOUNT_DMODE (0x1ULL << CSR_ICOUNT_DMODE_OFFSET) +/* + * When set, enable this trigger in VS-mode. + * This bit is hard-wired to 0 if the hart does not support + * virtualization mode. + */ +#define CSR_ICOUNT_VS_OFFSET 26 +#define CSR_ICOUNT_VS_LENGTH 1 +#define CSR_ICOUNT_VS (0x1ULL << CSR_ICOUNT_VS_OFFSET) +/* + * When set, enable this trigger in VU-mode. + * This bit is hard-wired to 0 if the hart does not support + * virtualization mode. + */ +#define CSR_ICOUNT_VU_OFFSET 25 +#define CSR_ICOUNT_VU_LENGTH 1 +#define CSR_ICOUNT_VU (0x1ULL << CSR_ICOUNT_VU_OFFSET) /* * If this bit is implemented, the hardware sets it when this * trigger matches. The trigger's user can set or clear it at any @@ -696,29 +1090,38 @@ /* * When count is decremented to 0, the trigger fires. Instead of * changing \FcsrIcountCount from 1 to 0, it is also acceptable for hardware to - * clear \FcsrMcontrolM, \FcsrMcontrolS, and \FcsrMcontrolU. This allows \FcsrIcountCount to be hard-wired + * clear \FcsrIcountM, \FcsrIcountS, \FcsrIcountU, \FcsrIcountVs, and + * \FcsrIcountVu. This allows \FcsrIcountCount to be hard-wired * to 1 if this register just exists for single step. */ #define CSR_ICOUNT_COUNT_OFFSET 10 #define CSR_ICOUNT_COUNT_LENGTH 14 #define CSR_ICOUNT_COUNT (0x3fffULL << CSR_ICOUNT_COUNT_OFFSET) /* - * When set, every instruction completed in or trap taken from - * M-mode decrements \FcsrIcountCount by 1. + * When set, enable this trigger in M-mode. */ #define CSR_ICOUNT_M_OFFSET 9 #define CSR_ICOUNT_M_LENGTH 1 #define CSR_ICOUNT_M (0x1ULL << CSR_ICOUNT_M_OFFSET) /* - * When set, every instruction completed in or trap taken from - * S-mode decrements \FcsrIcountCount by 1. + * This bit becomes set when \FcsrIcountCount is decremented from 1 + * to 0. It is cleared when the trigger fires. + */ +#define CSR_ICOUNT_PENDING_OFFSET 8 +#define CSR_ICOUNT_PENDING_LENGTH 1 +#define CSR_ICOUNT_PENDING (0x1ULL << CSR_ICOUNT_PENDING_OFFSET) +/* + * When set, enable this trigger in S/HS-mode. + * This bit is hard-wired to 0 if the hart does not support + * S-mode. */ #define CSR_ICOUNT_S_OFFSET 7 #define CSR_ICOUNT_S_LENGTH 1 #define CSR_ICOUNT_S (0x1ULL << CSR_ICOUNT_S_OFFSET) /* - * When set, every instruction completed in or trap taken from - * U-mode decrements \FcsrIcountCount by 1. + * When set, enable this trigger in U-mode. + * This bit is hard-wired to 0 if the hart does not support + * U-mode. */ #define CSR_ICOUNT_U_OFFSET 6 #define CSR_ICOUNT_U_LENGTH 1 @@ -747,6 +1150,24 @@ #define CSR_ITRIGGER_HIT_OFFSET (XLEN-6) #define CSR_ITRIGGER_HIT_LENGTH 1 #define CSR_ITRIGGER_HIT (0x1ULL << CSR_ITRIGGER_HIT_OFFSET) +/* + * When set, enable this trigger for interrupts that are taken from VS + * mode. + * This bit is hard-wired to 0 if the hart does not support + * virtualization mode. + */ +#define CSR_ITRIGGER_VS_OFFSET 12 +#define CSR_ITRIGGER_VS_LENGTH 1 +#define CSR_ITRIGGER_VS (0x1ULL << CSR_ITRIGGER_VS_OFFSET) +/* + * When set, enable this trigger for interrupts that are taken from VU + * mode. + * This bit is hard-wired to 0 if the hart does not support + * virtualization mode. + */ +#define CSR_ITRIGGER_VU_OFFSET 11 +#define CSR_ITRIGGER_VU_LENGTH 1 +#define CSR_ITRIGGER_VU (0x1ULL << CSR_ITRIGGER_VU_OFFSET) /* * When set, enable this trigger for interrupts that are taken from M * mode. @@ -755,8 +1176,10 @@ #define CSR_ITRIGGER_M_LENGTH 1 #define CSR_ITRIGGER_M (0x1ULL << CSR_ITRIGGER_M_OFFSET) /* - * When set, enable this trigger for interrupts that are taken from S + * When set, enable this trigger for interrupts that are taken from S/HS * mode. + * This bit is hard-wired to 0 if the hart does not support + * S-mode. */ #define CSR_ITRIGGER_S_OFFSET 7 #define CSR_ITRIGGER_S_LENGTH 1 @@ -764,6 +1187,8 @@ /* * When set, enable this trigger for interrupts that are taken from U * mode. + * This bit is hard-wired to 0 if the hart does not support + * U-mode. */ #define CSR_ITRIGGER_U_OFFSET 6 #define CSR_ITRIGGER_U_LENGTH 1 @@ -792,9 +1217,27 @@ #define CSR_ETRIGGER_HIT_OFFSET (XLEN-6) #define CSR_ETRIGGER_HIT_LENGTH 1 #define CSR_ETRIGGER_HIT (0x1ULL << CSR_ETRIGGER_HIT_OFFSET) +/* + * When set, enable this trigger for exceptions that are taken from VS + * mode. + * This bit is hard-wired to 0 if the hart does not support + * virtualization mode. + */ +#define CSR_ETRIGGER_VS_OFFSET 12 +#define CSR_ETRIGGER_VS_LENGTH 1 +#define CSR_ETRIGGER_VS (0x1ULL << CSR_ETRIGGER_VS_OFFSET) +/* + * When set, enable this trigger for exceptions that are taken from VU + * mode. + * This bit is hard-wired to 0 if the hart does not support + * virtualization mode. + */ +#define CSR_ETRIGGER_VU_OFFSET 11 +#define CSR_ETRIGGER_VU_LENGTH 1 +#define CSR_ETRIGGER_VU (0x1ULL << CSR_ETRIGGER_VU_OFFSET) /* * When set, non-maskable interrupts cause this - * trigger to fire, regardless of the values of \FcsrMcontrolM, \FcsrMcontrolS, and \FcsrMcontrolU. + * trigger to fire, regardless of the values of \FcsrEtriggerM, \FcsrEtriggerS, and \FcsrEtriggerU. */ #define CSR_ETRIGGER_NMI_OFFSET 10 #define CSR_ETRIGGER_NMI_LENGTH 1 @@ -807,8 +1250,10 @@ #define CSR_ETRIGGER_M_LENGTH 1 #define CSR_ETRIGGER_M (0x1ULL << CSR_ETRIGGER_M_OFFSET) /* - * When set, enable this trigger for exceptions that are taken from S + * When set, enable this trigger for exceptions that are taken from S/HS * mode. + * This bit is hard-wired to 0 if the hart does not support + * S-mode. */ #define CSR_ETRIGGER_S_OFFSET 7 #define CSR_ETRIGGER_S_LENGTH 1 @@ -816,6 +1261,8 @@ /* * When set, enable this trigger for exceptions that are taken from U * mode. + * This bit is hard-wired to 0 if the hart does not support + * U-mode. */ #define CSR_ETRIGGER_U_OFFSET 6 #define CSR_ETRIGGER_U_LENGTH 1 @@ -827,22 +1274,79 @@ #define CSR_ETRIGGER_ACTION_OFFSET 0 #define CSR_ETRIGGER_ACTION_LENGTH 6 #define CSR_ETRIGGER_ACTION (0x3fULL << CSR_ETRIGGER_ACTION_OFFSET) +#define CSR_TMEXTTRIGGER 0x7a1 +#define CSR_TMEXTTRIGGER_TYPE_OFFSET (XLEN-4) +#define CSR_TMEXTTRIGGER_TYPE_LENGTH 4 +#define CSR_TMEXTTRIGGER_TYPE (0xfULL << CSR_TMEXTTRIGGER_TYPE_OFFSET) +#define CSR_TMEXTTRIGGER_DMODE_OFFSET (XLEN-5) +#define CSR_TMEXTTRIGGER_DMODE_LENGTH 1 +#define CSR_TMEXTTRIGGER_DMODE (0x1ULL << CSR_TMEXTTRIGGER_DMODE_OFFSET) +/* + * If this bit is implemented, the hardware sets it when this + * trigger matches. The trigger's user can set or clear it at any + * time. It is used to determine which + * trigger(s) matched. If the bit is not implemented, it is always 0 + * and writing it has no effect. + */ +#define CSR_TMEXTTRIGGER_HIT_OFFSET (XLEN-6) +#define CSR_TMEXTTRIGGER_HIT_LENGTH 1 +#define CSR_TMEXTTRIGGER_HIT (0x1ULL << CSR_TMEXTTRIGGER_HIT_OFFSET) +/* + * This optional bit, when set, causes this trigger to fire whenever an attached + * interrupt controller signals a trigger. + */ +#define CSR_TMEXTTRIGGER_INTCTL_OFFSET 22 +#define CSR_TMEXTTRIGGER_INTCTL_LENGTH 1 +#define CSR_TMEXTTRIGGER_INTCTL (0x1ULL << CSR_TMEXTTRIGGER_INTCTL_OFFSET) +/* + * Selects any combination of up to 16 external debug trigger inputs + * that cause this trigger to fire. + */ +#define CSR_TMEXTTRIGGER_SELECT_OFFSET 6 +#define CSR_TMEXTTRIGGER_SELECT_LENGTH 16 +#define CSR_TMEXTTRIGGER_SELECT (0xffffULL << CSR_TMEXTTRIGGER_SELECT_OFFSET) +/* + * The action to take when the trigger fires. The values are explained + * in Table~\ref{tab:action}. + */ +#define CSR_TMEXTTRIGGER_ACTION_OFFSET 0 +#define CSR_TMEXTTRIGGER_ACTION_LENGTH 6 +#define CSR_TMEXTTRIGGER_ACTION (0x3fULL << CSR_TMEXTTRIGGER_ACTION_OFFSET) #define CSR_TEXTRA32 0x7a3 /* - * Data used together with \FcsrTextraThirtytwoMselect. + * Data used together with \FcsrTextraThirtytwoMhselect. */ -#define CSR_TEXTRA32_MVALUE_OFFSET 26 -#define CSR_TEXTRA32_MVALUE_LENGTH 6 -#define CSR_TEXTRA32_MVALUE (0x3fU << CSR_TEXTRA32_MVALUE_OFFSET) +#define CSR_TEXTRA32_MHVALUE_OFFSET 26 +#define CSR_TEXTRA32_MHVALUE_LENGTH 6 +#define CSR_TEXTRA32_MHVALUE (0x3fU << CSR_TEXTRA32_MHVALUE_OFFSET) /* - * 0: Ignore \FcsrTextraThirtytwoMvalue. + * 0: Ignore \FcsrTextraThirtytwoMhvalue. * - * 1: This trigger will only match if the low bits of - * \RcsrMcontext equal \FcsrTextraThirtytwoMvalue. + * 4: This trigger will only match if the low bits of + * \RcsrMcontext/\RcsrHcontext equal \FcsrTextraThirtytwoMhvalue. + * + * 1, 5: This trigger will only match if the low bits of + * \RcsrMcontext/\RcsrHcontext equal \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}. + * + * 2, 6: This trigger will only match if VMID in hgatp equals the lower VMIDMAX + * (defined in the Privileged Spec) bits of \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}. + * + * 3, 7: Reserved. + * + * If the H extension is not supported, the only legal values are 0 and 4. */ -#define CSR_TEXTRA32_MSELECT_OFFSET 25 -#define CSR_TEXTRA32_MSELECT_LENGTH 1 -#define CSR_TEXTRA32_MSELECT (0x1U << CSR_TEXTRA32_MSELECT_OFFSET) +#define CSR_TEXTRA32_MHSELECT_OFFSET 23 +#define CSR_TEXTRA32_MHSELECT_LENGTH 3 +#define CSR_TEXTRA32_MHSELECT (0x7U << CSR_TEXTRA32_MHSELECT_OFFSET) +/* + * When the least significant bit of this field is 1, it causes bits 7:0 + * in the comparison to be ignored, when \FcsrTextraThirtytwoSselect=1. + * When the next most significant bit of this field is 1, it causes bits 15:8 + * to be ignored in the comparison, when \FcsrTextraThirtytwoSselect=1. + */ +#define CSR_TEXTRA32_SBYTEMASK_OFFSET 18 +#define CSR_TEXTRA32_SBYTEMASK_LENGTH 2 +#define CSR_TEXTRA32_SBYTEMASK (0x3U << CSR_TEXTRA32_SBYTEMASK_OFFSET) /* * Data used together with \FcsrTextraThirtytwoSselect. * @@ -857,7 +1361,8 @@ * 1: This trigger will only match if the low bits of * \RcsrScontext equal \FcsrTextraThirtytwoSvalue. * - * 2: This trigger will only match if \Fasid in \Rsatp + * 2: This trigger will only match if the currently active ASID + * value, from either \Rsatp or \Rvsatp, * equals the lower ASIDMAX (defined in the Privileged Spec) bits of * \FcsrTextraThirtytwoSvalue. * @@ -867,12 +1372,23 @@ #define CSR_TEXTRA32_SSELECT_LENGTH 2 #define CSR_TEXTRA32_SSELECT (0x3U << CSR_TEXTRA32_SSELECT_OFFSET) #define CSR_TEXTRA64 0x7a3 -#define CSR_TEXTRA64_MVALUE_OFFSET 51 -#define CSR_TEXTRA64_MVALUE_LENGTH 13 -#define CSR_TEXTRA64_MVALUE (0x1fffULL << CSR_TEXTRA64_MVALUE_OFFSET) -#define CSR_TEXTRA64_MSELECT_OFFSET 50 -#define CSR_TEXTRA64_MSELECT_LENGTH 1 -#define CSR_TEXTRA64_MSELECT (0x1ULL << CSR_TEXTRA64_MSELECT_OFFSET) +#define CSR_TEXTRA64_MHVALUE_OFFSET 51 +#define CSR_TEXTRA64_MHVALUE_LENGTH 13 +#define CSR_TEXTRA64_MHVALUE (0x1fffULL << CSR_TEXTRA64_MHVALUE_OFFSET) +#define CSR_TEXTRA64_MHSELECT_OFFSET 48 +#define CSR_TEXTRA64_MHSELECT_LENGTH 3 +#define CSR_TEXTRA64_MHSELECT (0x7ULL << CSR_TEXTRA64_MHSELECT_OFFSET) +/* + * When the least significant bit of this field is 1, it causes bits 7:0 + * in the comparison to be ignored, when \FcsrTextraSixtyfourSselect=1. + * Likewise, the second bit controls the comparison of bits 15:8, + * third bit controls the comparison of bits 23:16, + * fourth bit controls the comparison of bits 31:24, and + * fifth bit controls the comparison of bits 33:32. + */ +#define CSR_TEXTRA64_SBYTEMASK_OFFSET 36 +#define CSR_TEXTRA64_SBYTEMASK_LENGTH 5 +#define CSR_TEXTRA64_SBYTEMASK (0x1fULL << CSR_TEXTRA64_SBYTEMASK_OFFSET) #define CSR_TEXTRA64_SVALUE_OFFSET 2 #define CSR_TEXTRA64_SVALUE_LENGTH 34 #define CSR_TEXTRA64_SVALUE (0x3ffffffffULL << CSR_TEXTRA64_SVALUE_OFFSET) @@ -880,6 +1396,24 @@ #define CSR_TEXTRA64_SSELECT_LENGTH 2 #define CSR_TEXTRA64_SSELECT (0x3ULL << CSR_TEXTRA64_SSELECT_OFFSET) #define DM_DMSTATUS 0x11 +/* + * 0: Unimplemented, or \FdmDmcontrolNdmreset is zero and no ndmreset is currently + * in progress. + * + * 1: \FdmDmcontrolNdmreset is currently nonzero, or there is an ndmreset in progress. + */ +#define DM_DMSTATUS_NDMRESETPENDING_OFFSET 24 +#define DM_DMSTATUS_NDMRESETPENDING_LENGTH 1 +#define DM_DMSTATUS_NDMRESETPENDING (0x1U << DM_DMSTATUS_NDMRESETPENDING_OFFSET) +/* + * 0: The per-hart {\tt unavail} bits reflect the current state of the hart. + * + * 1: The per-hart {\tt unavail} bits are sticky. Once they are set, they will + * not clear until the debugger acknowledges them using \FdmDmcontrolAckunavail. + */ +#define DM_DMSTATUS_STICKYUNAVAIL_OFFSET 23 +#define DM_DMSTATUS_STICKYUNAVAIL_LENGTH 1 +#define DM_DMSTATUS_STICKYUNAVAIL (0x1U << DM_DMSTATUS_STICKYUNAVAIL_OFFSET) /* * If 1, then there is an implicit {\tt ebreak} instruction at the * non-existent word immediately after the Program Buffer. This saves @@ -921,26 +1455,30 @@ #define DM_DMSTATUS_ANYRESUMEACK (0x1U << DM_DMSTATUS_ANYRESUMEACK_OFFSET) /* * This field is 1 when all currently selected harts do not exist in - * this platform. + * this hardware platform. */ #define DM_DMSTATUS_ALLNONEXISTENT_OFFSET 15 #define DM_DMSTATUS_ALLNONEXISTENT_LENGTH 1 #define DM_DMSTATUS_ALLNONEXISTENT (0x1U << DM_DMSTATUS_ALLNONEXISTENT_OFFSET) /* * This field is 1 when any currently selected hart does not exist in - * this platform. + * this hardware platform. */ #define DM_DMSTATUS_ANYNONEXISTENT_OFFSET 14 #define DM_DMSTATUS_ANYNONEXISTENT_LENGTH 1 #define DM_DMSTATUS_ANYNONEXISTENT (0x1U << DM_DMSTATUS_ANYNONEXISTENT_OFFSET) /* - * This field is 1 when all currently selected harts are unavailable. + * This field is 1 when all currently selected harts are + * unavailable, or (if \FdmDmstatusStickyunavail is 1) were + * unavailable without that being acknowledged. */ #define DM_DMSTATUS_ALLUNAVAIL_OFFSET 13 #define DM_DMSTATUS_ALLUNAVAIL_LENGTH 1 #define DM_DMSTATUS_ALLUNAVAIL (0x1U << DM_DMSTATUS_ALLUNAVAIL_OFFSET) /* - * This field is 1 when any currently selected hart is unavailable. + * This field is 1 when any currently selected hart is unavailable, + * or (if \FdmDmstatusStickyunavail is 1) was unavailable without + * that being acknowledged. */ #define DM_DMSTATUS_ANYUNAVAIL_OFFSET 12 #define DM_DMSTATUS_ANYUNAVAIL_LENGTH 1 @@ -1020,7 +1558,7 @@ * 2: There is a Debug Module and it conforms to version 0.13 of this * specification. * - * 3: There is a Debug Module and it conforms to version 0.14 of this + * 3: There is a Debug Module and it conforms to version 1.0 of this * specification. * * 15: There is a Debug Module but it does not conform to any @@ -1082,6 +1620,16 @@ #define DM_DMCONTROL_ACKHAVERESET_OFFSET 28 #define DM_DMCONTROL_ACKHAVERESET_LENGTH 1 #define DM_DMCONTROL_ACKHAVERESET (0x1U << DM_DMCONTROL_ACKHAVERESET_OFFSET) +/* + * 0: No effect. + * + * 1: Clears {\tt unavail} for any selected harts. + * + * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + */ +#define DM_DMCONTROL_ACKUNAVAIL_OFFSET 27 +#define DM_DMCONTROL_ACKUNAVAIL_LENGTH 1 +#define DM_DMCONTROL_ACKUNAVAIL (0x1U << DM_DMCONTROL_ACKUNAVAIL_OFFSET) /* * Selects the definition of currently selected harts. * @@ -1113,6 +1661,25 @@ #define DM_DMCONTROL_HARTSELHI_OFFSET 6 #define DM_DMCONTROL_HARTSELHI_LENGTH 10 #define DM_DMCONTROL_HARTSELHI (0x3ffU << DM_DMCONTROL_HARTSELHI_OFFSET) +/* + * This optional field sets \Fkeepalive for all currently selected + * harts, unless \FdmDmcontrolClrkeepalive is simultaneously set to + * 1. + * + * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + */ +#define DM_DMCONTROL_SETKEEPALIVE_OFFSET 5 +#define DM_DMCONTROL_SETKEEPALIVE_LENGTH 1 +#define DM_DMCONTROL_SETKEEPALIVE (0x1U << DM_DMCONTROL_SETKEEPALIVE_OFFSET) +/* + * This optional field clears \Fkeepalive for all currently selected + * harts. + * + * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel. + */ +#define DM_DMCONTROL_CLRKEEPALIVE_OFFSET 4 +#define DM_DMCONTROL_CLRKEEPALIVE_LENGTH 1 +#define DM_DMCONTROL_CLRKEEPALIVE (0x1U << DM_DMCONTROL_CLRKEEPALIVE_OFFSET) /* * This optional field writes the halt-on-reset request bit for all * currently selected harts, unless \FdmDmcontrolClrresethaltreq is @@ -1139,10 +1706,10 @@ #define DM_DMCONTROL_CLRRESETHALTREQ (0x1U << DM_DMCONTROL_CLRRESETHALTREQ_OFFSET) /* * This bit controls the reset signal from the DM to the rest of the - * system. The signal should reset every part of the system, including + * hardware platform. The signal should reset every part of the hardware platform, including * every hart, except for the DM and any logic required to access the * DM. - * To perform a system reset the debugger writes 1, + * To perform a hardware platform reset the debugger writes 1, * and then writes 0 * to deassert the reset. */ @@ -1151,23 +1718,27 @@ #define DM_DMCONTROL_NDMRESET (0x1U << DM_DMCONTROL_NDMRESET_OFFSET) /* * This bit serves as a reset signal for the Debug Module itself. + * After changing the value of this bit, the debugger must poll + * \RdmDmcontrol until \FdmDmcontrolDmactive has taken the requested value + * before performing any action that assumes the requested \FdmDmcontrolDmactive + * state change has completed. Hardware may + * take an arbitrarily long time to complete activation or deactivation and will + * indicate completion by setting \FdmDmcontrolDmactive to the requested value. * * 0: The module's state, including authentication mechanism, * takes its reset values (the \FdmDmcontrolDmactive bit is the only bit which can * be written to something other than its reset value). Any accesses - * to the module may fail. Specifically, \FdmDmstatusVersion may not return + * to the module may fail. Specifically, \FdmDmstatusVersion might not return * correct data. * - * 1: The module functions normally. After writing 1, the debugger should - * poll \RdmDmcontrol until \FdmDmcontrolDmactive is high. Hardware may - * take an arbitrarily long time to initialize and will indicate completion - * by setting dmactive to 1. + * 1: The module functions normally. * * No other mechanism should exist that may result in resetting the * Debug Module after power up. * - * A debugger may pulse this bit low to get the Debug Module into a - * known state. + * To place the Debug Module into a known state, a debugger may write 0 to \FdmDmcontrolDmactive, + * poll until \FdmDmcontrolDmactive is observed 0, write 1 to \FdmDmcontrolDmactive, and + * poll until \FdmDmcontrolDmactive is observed 1. * * Implementations may pay attention to this bit to further aid * debugging, for example by preventing the Debug Module from being @@ -1225,7 +1796,7 @@ #define DM_HAWINDOWSEL 0x14 /* * The high bits of this field may be tied to 0, depending on how large - * the array mask register is. E.g.\ on a system with 48 harts only bit 0 + * the array mask register is. E.g.\ on a hardware platform with 48 harts only bit 0 * of this field may actually be writable. */ #define DM_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0 @@ -1361,11 +1932,35 @@ #define DM_DATA0_DATA_OFFSET 0 #define DM_DATA0_DATA_LENGTH 32 #define DM_DATA0_DATA (0xffffffffU << DM_DATA0_DATA_OFFSET) +#define DM_DATA1 0x05 +#define DM_DATA2 0x06 +#define DM_DATA3 0x07 +#define DM_DATA4 0x08 +#define DM_DATA5 0x09 +#define DM_DATA6 0x0a +#define DM_DATA7 0x0b +#define DM_DATA8 0x0c +#define DM_DATA9 0x0d +#define DM_DATA10 0x0e #define DM_DATA11 0x0f #define DM_PROGBUF0 0x20 #define DM_PROGBUF0_DATA_OFFSET 0 #define DM_PROGBUF0_DATA_LENGTH 32 #define DM_PROGBUF0_DATA (0xffffffffU << DM_PROGBUF0_DATA_OFFSET) +#define DM_PROGBUF1 0x21 +#define DM_PROGBUF2 0x22 +#define DM_PROGBUF3 0x23 +#define DM_PROGBUF4 0x24 +#define DM_PROGBUF5 0x25 +#define DM_PROGBUF6 0x26 +#define DM_PROGBUF7 0x27 +#define DM_PROGBUF8 0x28 +#define DM_PROGBUF9 0x29 +#define DM_PROGBUF10 0x2a +#define DM_PROGBUF11 0x2b +#define DM_PROGBUF12 0x2c +#define DM_PROGBUF13 0x2d +#define DM_PROGBUF14 0x2e #define DM_PROGBUF15 0x2f #define DM_AUTHDATA 0x30 #define DM_AUTHDATA_DATA_OFFSET 0 @@ -1381,20 +1976,20 @@ #define DM_DMCS2_GROUPTYPE_LENGTH 1 #define DM_DMCS2_GROUPTYPE (0x1U << DM_DMCS2_GROUPTYPE_OFFSET) /* - * This field contains the currently selected external trigger. + * This field contains the currently selected DM external trigger. * * If a non-existent trigger value is written here, the hardware will - * change it to a valid one or 0 if no external triggers exist. + * change it to a valid one or 0 if no DM external triggers exist. */ -#define DM_DMCS2_EXTTRIGGER_OFFSET 7 -#define DM_DMCS2_EXTTRIGGER_LENGTH 4 -#define DM_DMCS2_EXTTRIGGER (0xfU << DM_DMCS2_EXTTRIGGER_OFFSET) +#define DM_DMCS2_DMEXTTRIGGER_OFFSET 7 +#define DM_DMCS2_DMEXTTRIGGER_LENGTH 4 +#define DM_DMCS2_DMEXTTRIGGER (0xfU << DM_DMCS2_DMEXTTRIGGER_OFFSET) /* * When \FdmDmcsTwoHgselect is 0, contains the group of the hart * specified by \Fhartsel. * - * When \FdmDmcsTwoHgselect is 1, contains the group of the external - * trigger selected by \FdmDmcsTwoExttrigger. + * When \FdmDmcsTwoHgselect is 1, contains the group of the DM external + * trigger selected by \FdmDmcsTwoDmexttrigger. * * Writes only have an effect if \FdmDmcsTwoHgwrite is also written 1. * @@ -1409,15 +2004,15 @@ #define DM_DMCS2_GROUP_LENGTH 5 #define DM_DMCS2_GROUP (0x1fU << DM_DMCS2_GROUP_OFFSET) /* - * When \FdmDmcsTwoHgselect is 0, writing 1 changes the group of all - * selected harts to the value written to \FdmDmcsTwoGroup. - * * When 1 is written and \FdmDmcsTwoHgselect is 0, for every selected * hart the DM will change its group to the value written to \FdmDmcsTwoGroup, * if the hardware supports that group for that hart. + * Implementations may also change the group of a minimal set of + * unselected harts in the same way, if that is necessary due to + * a hardware limitation. * * When 1 is written and \FdmDmcsTwoHgselect is 1, the DM will change - * the group of the external trigger selected by \FdmDmcsTwoExttrigger + * the group of the DM external trigger selected by \FdmDmcsTwoDmexttrigger * to the value written to \FdmDmcsTwoGroup, if the hardware supports * that group for that trigger. * @@ -1429,9 +2024,9 @@ /* * 0: Operate on harts. * - * 1: Operate on external triggers. + * 1: Operate on DM external triggers. * - * If there are no external triggers, this field must be tied to 0. + * If there are no DM external triggers, this field must be tied to 0. */ #define DM_DMCS2_HGSELECT_OFFSET 0 #define DM_DMCS2_HGSELECT_LENGTH 1 @@ -1654,6 +2249,20 @@ #define DM_SBDATA3_DATA (0xffffffffU << DM_SBDATA3_DATA_OFFSET) #define DM_CUSTOM 0x1f #define DM_CUSTOM0 0x70 +#define DM_CUSTOM1 0x71 +#define DM_CUSTOM2 0x72 +#define DM_CUSTOM3 0x73 +#define DM_CUSTOM4 0x74 +#define DM_CUSTOM5 0x75 +#define DM_CUSTOM6 0x76 +#define DM_CUSTOM7 0x77 +#define DM_CUSTOM8 0x78 +#define DM_CUSTOM9 0x79 +#define DM_CUSTOM10 0x7a +#define DM_CUSTOM11 0x7b +#define DM_CUSTOM12 0x7c +#define DM_CUSTOM13 0x7d +#define DM_CUSTOM14 0x7e #define DM_CUSTOM15 0x7f #define SHORTNAME 0x123 /* @@ -1689,9 +2298,10 @@ * 0: No effect. This variant must be supported. * * 1: After a successful register access, \FacAccessregisterRegno is - * incremented (wrapping around to 0). Supporting this variant is - * optional. It is undefined whether the increment happens when - * \FacAccessregisterTransfer is 0. + * incremented. Incrementing past the highest supported value + * causes \FacAccessregisterRegno to become \unspecified. Supporting + * this variant is optional. It is undefined whether the increment + * happens when \FacAccessregisterTransfer is 0. */ #define AC_ACCESS_REGISTER_AARPOSTINCREMENT_OFFSET 19 #define AC_ACCESS_REGISTER_AARPOSTINCREMENT_LENGTH 1 @@ -1758,7 +2368,11 @@ * 0: Addresses are physical (to the hart they are performed on). * * 1: Addresses are virtual, and translated the way they would be from - * M-mode, with \FcsrMcontrolMprv set. + * M-mode, with \FcsrMstatusMprv set. + * + * Debug Modules on systems without address translation (i.e. virtual addresses equal physical) + * may optionally allow \FacAccessmemoryAamvirtual set to 1, which would produce the same result as + * that same abstract command with \FacAccessmemoryAamvirtual cleared. */ #define AC_ACCESS_MEMORY_AAMVIRTUAL_OFFSET 23 #define AC_ACCESS_MEMORY_AAMVIRTUAL_LENGTH 1 @@ -1806,6 +2420,16 @@ #define AC_ACCESS_MEMORY_TARGET_SPECIFIC_LENGTH 2 #define AC_ACCESS_MEMORY_TARGET_SPECIFIC (0x3U << AC_ACCESS_MEMORY_TARGET_SPECIFIC_OFFSET) #define VIRT_PRIV virtual +/* + * Contains the virtualization mode the hart was operating in when Debug + * Mode was entered. The encoding is described in Table \ref{tab:privlevel}, + * and matches the virtualization mode encoding from the Privileged Spec. + * A user can write this value to change the hart's virtualization mode + * when exiting Debug Mode. + */ +#define VIRT_PRIV_V_OFFSET 2 +#define VIRT_PRIV_V_LENGTH 1 +#define VIRT_PRIV_V (0x1U << VIRT_PRIV_V_OFFSET) /* * Contains the privilege level the hart was operating in when Debug * Mode was entered. The encoding is described in Table diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h index 998290cb9..8faa154ba 100644 --- a/src/target/riscv/opcodes.h +++ b/src/target/riscv/opcodes.h @@ -17,214 +17,202 @@ static uint32_t bit(uint32_t value, unsigned int b) return (value >> b) & 1; } +static uint32_t inst_rd(uint32_t r) __attribute__ ((unused)); +static uint32_t inst_rd(uint32_t r) +{ + return bits(r, 4, 0) << 7; +} + +static uint32_t inst_rs1(uint32_t r) __attribute__ ((unused)); +static uint32_t inst_rs1(uint32_t r) +{ + return bits(r, 4, 0) << 15; +} + +static uint32_t inst_rs2(uint32_t r) __attribute__ ((unused)); +static uint32_t inst_rs2(uint32_t r) +{ + return bits(r, 4, 0) << 20; +} + +static uint32_t imm_i(uint32_t imm) __attribute__ ((unused)); +static uint32_t imm_i(uint32_t imm) +{ + return bits(imm, 11, 0) << 20; +} + +static uint32_t imm_s(uint32_t imm) __attribute__ ((unused)); +static uint32_t imm_s(uint32_t imm) +{ + return (bits(imm, 4, 0) << 7) | (bits(imm, 11, 5) << 25); +} + +static uint32_t imm_b(uint32_t imm) __attribute__ ((unused)); +static uint32_t imm_b(uint32_t imm) +{ + return (bit(imm, 11) << 7) | (bits(imm, 4, 1) << 8) | (bits(imm, 10, 5) << 25) | (bit(imm, 12) << 31); +} + +static uint32_t imm_u(uint32_t imm) __attribute__ ((unused)); +static uint32_t imm_u(uint32_t imm) +{ + return bits(imm, 31, 12) << 12; +} + +static uint32_t imm_j(uint32_t imm) __attribute__ ((unused)); +static uint32_t imm_j(uint32_t imm) +{ + return (bits(imm, 19, 12) << 12) | (bit(imm, 11) << 20) | (bits(imm, 10, 1) << 21) | (bit(imm, 20) << 31); +} + static uint32_t jal(unsigned int rd, uint32_t imm) __attribute__ ((unused)); static uint32_t jal(unsigned int rd, uint32_t imm) { - return (bit(imm, 20) << 31) | - (bits(imm, 10, 1) << 21) | - (bit(imm, 11) << 20) | - (bits(imm, 19, 12) << 12) | - (rd << 7) | - MATCH_JAL; + return imm_j(imm) | inst_rd(rd) | MATCH_JAL; } static uint32_t csrsi(unsigned int csr, uint16_t imm) __attribute__ ((unused)); static uint32_t csrsi(unsigned int csr, uint16_t imm) { - return (csr << 20) | - (bits(imm, 4, 0) << 15) | - MATCH_CSRRSI; + return imm_i(csr) | inst_rs1(imm) | MATCH_CSRRSI; } static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 5) << 25) | - (src << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_SW; + return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SW; } static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 5) << 25) | - (src << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_SD; + return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SD; } static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 5) << 25) | - (src << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_SH; + return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SH; } static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 5) << 25) | - (src << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_SB; + return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SB; } static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(rd, 4, 0) << 7) | - MATCH_LD; + return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LD; } static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(rd, 4, 0) << 7) | - MATCH_LW; + return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LW; } static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(rd, 4, 0) << 7) | - MATCH_LH; + return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LH; } static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(rd, 4, 0) << 7) | - MATCH_LB; + return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LB; } static uint32_t csrw(unsigned int source, unsigned int csr) __attribute__ ((unused)); static uint32_t csrw(unsigned int source, unsigned int csr) { - return (csr << 20) | (source << 15) | MATCH_CSRRW; + return imm_i(csr) | inst_rs1(source) | MATCH_CSRRW; } static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused)); static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) { - return (bits(imm, 11, 0) << 20) | - (src << 15) | - (dest << 7) | - MATCH_ADDI; + return imm_i(imm) | inst_rs1(src) | inst_rd(dest) | MATCH_ADDI; } static uint32_t csrr(unsigned int rd, unsigned int csr) __attribute__ ((unused)); static uint32_t csrr(unsigned int rd, unsigned int csr) { - return (csr << 20) | (rd << 7) | MATCH_CSRRS; + return imm_i(csr) | inst_rd(rd) | MATCH_CSRRS; } static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused)); static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) { - return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRS; + return imm_i(csr) | inst_rs1(rs) | inst_rd(rd) | MATCH_CSRRS; } static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused)); static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) { - return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRW; + return imm_i(csr) | inst_rs1(rs) | inst_rd(rd) | MATCH_CSRRW; } static uint32_t csrrci(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused)); static uint32_t csrrci(unsigned int rd, unsigned int zimm, unsigned int csr) { - return (csr << 20) | (zimm << 15) | (rd << 7) | MATCH_CSRRCI; + return imm_i(csr) | inst_rs1(zimm) | inst_rd(rd) | MATCH_CSRRCI; } static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused)); static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr) { - return (csr << 20) | (zimm << 15) | (rd << 7) | MATCH_CSRRSI; + return imm_i(csr) | inst_rs1(zimm) | inst_rd(rd) | MATCH_CSRRSI; } static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 5) << 25) | - (bits(src, 4, 0) << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_FSW; + return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSW; } static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 5) << 25) | - (bits(src, 4, 0) << 20) | - (base << 15) | - (bits(offset, 4, 0) << 7) | - MATCH_FSD; + return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSD; } static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(dest, 4, 0) << 7) | - MATCH_FLW; + return imm_i(offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLW; } static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused)); static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) { - return (bits(offset, 11, 0) << 20) | - (base << 15) | - (bits(dest, 4, 0) << 7) | - MATCH_FLD; + return imm_i(offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLD; } static uint32_t fmv_x_w(unsigned dest, unsigned src) __attribute__ ((unused)); static uint32_t fmv_x_w(unsigned dest, unsigned src) { - return src << 15 | - dest << 7 | - MATCH_FMV_X_W; + return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_X_W; } static uint32_t fmv_x_d(unsigned dest, unsigned src) __attribute__ ((unused)); static uint32_t fmv_x_d(unsigned dest, unsigned src) { - return src << 15 | - dest << 7 | - MATCH_FMV_X_D; + return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_X_D; } static uint32_t fmv_w_x(unsigned dest, unsigned src) __attribute__ ((unused)); static uint32_t fmv_w_x(unsigned dest, unsigned src) { - return src << 15 | - dest << 7 | - MATCH_FMV_W_X; + return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_W_X; } static uint32_t fmv_d_x(unsigned dest, unsigned src) __attribute__ ((unused)); static uint32_t fmv_d_x(unsigned dest, unsigned src) { - return src << 15 | - dest << 7 | - MATCH_FMV_D_X; + return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_D_X; } static uint32_t ebreak(void) __attribute__ ((unused)); @@ -250,9 +238,7 @@ static uint32_t fence_i(void) static uint32_t lui(unsigned int dest, uint32_t imm) __attribute__ ((unused)); static uint32_t lui(unsigned int dest, uint32_t imm) { - return (bits(imm, 19, 0) << 12) | - (dest << 7) | - MATCH_LUI; + return imm_u(imm) | inst_rd(dest) | MATCH_LUI; } /* @@ -299,19 +285,13 @@ static uint32_t nop(void) static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused)); static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) { - return (bits(imm, 11, 0) << 20) | - (src << 15) | - (dest << 7) | - MATCH_XORI; + return imm_i(imm) | inst_rs1(src) | inst_rd(dest) | MATCH_XORI; } static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) __attribute__ ((unused)); static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) { - return (bits(shamt, 4, 0) << 20) | - (src << 15) | - (dest << 7) | - MATCH_SRLI; + return inst_rs2(shamt) | inst_rs1(src) | inst_rd(dest) | MATCH_SRLI; } static uint32_t fence(void) __attribute__((unused)); @@ -323,28 +303,25 @@ static uint32_t fence(void) static uint32_t auipc(unsigned int dest) __attribute__((unused)); static uint32_t auipc(unsigned int dest) { - return MATCH_AUIPC | (dest << 7); + return MATCH_AUIPC | inst_rd(dest); } static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm) __attribute__((unused)); static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm) { - return (bits(imm, 10, 0) << 20) | - (src << 15) | - (dest << 7) | - MATCH_VSETVLI; + return (bits(imm, 10, 0) << 20) | inst_rs1(src) | inst_rd(dest) | MATCH_VSETVLI; } static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) __attribute__((unused)); static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) { - return (vs2 << 20) | (rd << 7) | MATCH_VMV_X_S; + return inst_rs2(vs2) | inst_rd(rd) | MATCH_VMV_X_S; } static uint32_t vmv_s_x(unsigned int vd, unsigned int vs2) __attribute__((unused)); static uint32_t vmv_s_x(unsigned int vd, unsigned int rs1) { - return (rs1 << 15) | (vd << 7) | MATCH_VMV_S_X; + return inst_rs1(rs1) | inst_rd(vd) | MATCH_VMV_S_X; } static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2, @@ -352,6 +329,6 @@ static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2, static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2, unsigned int rs1, unsigned int vm) { - return (vm << 25) | (vs2 << 20) | (rs1 << 15) | (vd << 7) | - MATCH_VSLIDE1DOWN_VX; + return ((vm & 1) << 25) | inst_rs2(vs2) | inst_rs1(rs1) | inst_rd(vd) | MATCH_VSLIDE1DOWN_VX; } + diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h index 2fa925aff..62a04f093 100644 --- a/src/target/riscv/program.h +++ b/src/target/riscv/program.h @@ -40,20 +40,10 @@ int riscv_program_write(struct riscv_program *program); * program to execute. That's OK, just make sure this eventually terminates. * */ int riscv_program_exec(struct riscv_program *p, struct target *t); -int riscv_program_load(struct riscv_program *p, struct target *t); - -/* Clears a program, removing all the state associated with it. */ -int riscv_program_clear(struct riscv_program *p, struct target *t); /* A lower level interface, you shouldn't use this unless you have a reason. */ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i); -/* There is hardware support for saving at least one register. This register - * doesn't need to be saved/restored the usual way, which is useful during - * early initialization when we can't save/restore arbitrary registerrs to host - * memory. */ -int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save); - /* 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. */ diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c index 86a95f635..ae0b6e481 100644 --- a/src/target/riscv/riscv-011.c +++ b/src/target/riscv/riscv-011.c @@ -152,6 +152,9 @@ typedef enum slot { #define DMINFO_AUTHTYPE (3<<2) #define DMINFO_VERSION 3 +#define DMAUTHDATA0 0x12 +#define DMAUTHDATA1 0x13 + /*** Info about the core being debugged. ***/ #define DBUS_ADDRESS_UNKNOWN 0xffff @@ -216,8 +219,7 @@ typedef struct { static int poll_target(struct target *target, bool announce); static int riscv011_poll(struct target *target); -static int get_register(struct target *target, riscv_reg_t *value, int hartid, - int regid); +static int get_register(struct target *target, riscv_reg_t *value, int regid); /*** Utility functions. ***/ @@ -226,6 +228,8 @@ static int get_register(struct target *target, riscv_reg_t *value, int hartid, static riscv011_info_t *get_info(const struct target *target) { riscv_info_t *info = (riscv_info_t *) target->arch_info; + assert(info); + assert(info->version_specific); return (riscv011_info_t *) info->version_specific; } @@ -1230,7 +1234,7 @@ static int update_mstatus_actual(struct target *target) /* Force reading the register. In that process mstatus_actual will be * updated. */ riscv_reg_t mstatus; - return get_register(target, &mstatus, 0, GDB_REGNO_MSTATUS); + return get_register(target, &mstatus, GDB_REGNO_MSTATUS); } /*** OpenOCD target functions. ***/ @@ -1334,10 +1338,8 @@ static int register_write(struct target *target, unsigned int number, return ERROR_OK; } -static int get_register(struct target *target, riscv_reg_t *value, int hartid, - int regid) +static int get_register(struct target *target, riscv_reg_t *value, int regid) { - assert(hartid == 0); riscv011_info_t *info = get_info(target); maybe_write_tselect(target); @@ -1380,10 +1382,8 @@ static int get_register(struct target *target, riscv_reg_t *value, int hartid, return ERROR_OK; } -static int set_register(struct target *target, int hartid, int regid, - uint64_t value) +static int set_register(struct target *target, int regid, uint64_t value) { - assert(hartid == 0); return register_write(target, regid, value); } @@ -1523,7 +1523,7 @@ static int examine(struct target *target) } /* Pretend this is a 32-bit system until we have found out the true value. */ - r->xlen[0] = 32; + r->xlen = 32; /* Figure out XLEN, and test writing all of Debug RAM while we're at it. */ cache_set32(target, 0, xori(S1, ZERO, -1)); @@ -1551,11 +1551,11 @@ static int examine(struct target *target) uint32_t word1 = cache_get32(target, 1); riscv_info_t *generic_info = (riscv_info_t *) target->arch_info; if (word0 == 1 && word1 == 0) { - generic_info->xlen[0] = 32; + generic_info->xlen = 32; } else if (word0 == 0xffffffff && word1 == 3) { - generic_info->xlen[0] = 64; + generic_info->xlen = 64; } else if (word0 == 0xffffffff && word1 == 0xffffffff) { - generic_info->xlen[0] = 128; + generic_info->xlen = 128; } else { uint32_t exception = cache_get32(target, info->dramsize-1); LOG_ERROR("Failed to discover xlen; word0=0x%x, word1=0x%x, exception=0x%x", @@ -1565,11 +1565,11 @@ static int examine(struct target *target) } LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target)); - if (read_remote_csr(target, &r->misa[0], CSR_MISA) != ERROR_OK) { + if (read_remote_csr(target, &r->misa, 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_remote_csr(target, &r->misa[0], old_csr_misa) != ERROR_OK) { + if (read_remote_csr(target, &r->misa, 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); @@ -1591,7 +1591,7 @@ static int examine(struct target *target) for (size_t i = 0; i < 32; ++i) reg_cache_set(target, i, -1); LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64, - riscv_xlen(target), r->misa[0]); + riscv_xlen(target), r->misa); return ERROR_OK; } @@ -2294,21 +2294,95 @@ static int arch_state(struct target *target) return ERROR_OK; } +COMMAND_HELPER(riscv011_print_info, struct target *target) +{ + /* Abstract description. */ + riscv_print_info_line(CMD, "target", "memory.read_while_running8", 0); + riscv_print_info_line(CMD, "target", "memory.write_while_running8", 0); + riscv_print_info_line(CMD, "target", "memory.read_while_running16", 0); + riscv_print_info_line(CMD, "target", "memory.write_while_running16", 0); + riscv_print_info_line(CMD, "target", "memory.read_while_running32", 0); + riscv_print_info_line(CMD, "target", "memory.write_while_running32", 0); + riscv_print_info_line(CMD, "target", "memory.read_while_running64", 0); + riscv_print_info_line(CMD, "target", "memory.write_while_running64", 0); + riscv_print_info_line(CMD, "target", "memory.read_while_running128", 0); + riscv_print_info_line(CMD, "target", "memory.write_while_running128", 0); + + uint32_t dminfo = dbus_read(target, DMINFO); + riscv_print_info_line(CMD, "dm", "authenticated", get_field(dminfo, DMINFO_AUTHENTICATED)); + + return 0; +} + +static int wait_for_authbusy(struct target *target) +{ + time_t start = time(NULL); + while (1) { + uint32_t dminfo = dbus_read(target, DMINFO); + if (!get_field(dminfo, DMINFO_AUTHBUSY)) + break; + if (time(NULL) - start > riscv_command_timeout_sec) { + LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dminfo=0x%x). " + "Increase the timeout with riscv set_command_timeout_sec.", + riscv_command_timeout_sec, + dminfo); + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +static int riscv011_authdata_read(struct target *target, uint32_t *value, unsigned int index) +{ + if (index > 1) { + LOG_ERROR("Spec 0.11 only has a two authdata registers."); + return ERROR_FAIL; + } + + if (wait_for_authbusy(target) != ERROR_OK) + return ERROR_FAIL; + + uint16_t authdata_address = index ? DMAUTHDATA1 : DMAUTHDATA0; + *value = dbus_read(target, authdata_address); + + return ERROR_OK; +} + +static int riscv011_authdata_write(struct target *target, uint32_t value, unsigned int index) +{ + if (index > 1) { + LOG_ERROR("Spec 0.11 only has a two authdata registers."); + return ERROR_FAIL; + } + + if (wait_for_authbusy(target) != ERROR_OK) + return ERROR_FAIL; + + uint16_t authdata_address = index ? DMAUTHDATA1 : DMAUTHDATA0; + dbus_write(target, authdata_address, value); + + 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; + RISCV_INFO(generic_info); generic_info->get_register = get_register; generic_info->set_register = set_register; generic_info->read_memory = read_memory; + generic_info->authdata_read = &riscv011_authdata_read; + generic_info->authdata_write = &riscv011_authdata_write; + generic_info->print_info = &riscv011_print_info; 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; + generic_info->xlen = 32; riscv_init_registers(target); return ERROR_OK; diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index fdebdd413..006266a90 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -29,9 +29,6 @@ #include "asm.h" #include "batch.h" -#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, bool use_hasel); @@ -39,8 +36,8 @@ static void riscv013_clear_abstract_error(struct target *target); /* Implementations of the functions in riscv_info_t. */ 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); + riscv_reg_t *value, int rid); +static int riscv013_set_register(struct target *target, int regid, uint64_t value); static int riscv013_select_current_hart(struct target *target); static int riscv013_halt_prep(struct target *target); static int riscv013_halt_go(struct target *target); @@ -74,7 +71,6 @@ void write_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t uint32_t write_size, uint32_t sbcs); void read_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t *rd_buf, uint32_t read_size, uint32_t sbcs); -static int riscv013_test_compliance(struct target *target); /** * Since almost everything can be accomplish by scanning the dbus register, all @@ -206,6 +202,8 @@ typedef struct { bool abstract_read_fpr_supported; bool abstract_write_fpr_supported; + yes_no_maybe_t has_aampostincrement; + /* When a function returns some error due to a failure indicated by the * target in cmderr, the caller can look here to see what that error was. * (Compare with errno.) */ @@ -228,6 +226,8 @@ LIST_HEAD(dm_list); static riscv013_info_t *get_info(const struct target *target) { riscv_info_t *info = (riscv_info_t *) target->arch_info; + assert(info); + assert(info->version_specific); return (riscv013_info_t *) info->version_specific; } @@ -588,6 +588,8 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in, return ERROR_FAIL; } + keep_alive(); + time_t start = time(NULL); /* This first loop performs the request. Note that if for some reason this * stays busy, it is actually due to the previous access. */ @@ -690,7 +692,7 @@ int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus, return result; int dmstatus_version = get_field(*dmstatus, DM_DMSTATUS_VERSION); if (dmstatus_version != 2 && dmstatus_version != 3) { - LOG_ERROR("OpenOCD only supports Debug Module version 2 (0.13) and 3 (0.14), not " + LOG_ERROR("OpenOCD only supports Debug Module version 2 (0.13) and 3 (1.0), not " "%d (dmstatus=0x%x). This error might be caused by a JTAG " "signal issue. Try reducing the JTAG clock speed.", get_field(*dmstatus, DM_DMSTATUS_VERSION), *dmstatus); @@ -1324,7 +1326,7 @@ static int register_write_direct(struct target *target, unsigned number, scratch_mem_t scratch; bool use_scratch = false; if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && - riscv_supports_extension(target, riscv_current_hartid(target), 'D') && + riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) { /* There are no instructions to move all the bits from a register, so * we need to use some scratch RAM. */ @@ -1354,7 +1356,7 @@ static int register_write_direct(struct target *target, unsigned number, return ERROR_FAIL; if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { - if (riscv_supports_extension(target, riscv_current_hartid(target), 'D')) + if (riscv_supports_extension(target, 'D')) riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0)); else riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0)); @@ -1395,7 +1397,7 @@ static int register_write_direct(struct target *target, unsigned number, return exec_out; } -/** Return the cached value, or read from the target if necessary. */ +/** Read register value from the target. Also update the cached value. */ static int register_read(struct target *target, uint64_t *value, uint32_t number) { if (number == GDB_REGNO_ZERO) { @@ -1438,7 +1440,7 @@ 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) { - if (riscv_supports_extension(target, riscv_current_hartid(target), 'D') + if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) { /* There are no instructions to move all the bits from a * register, so we need to use some scratch RAM. */ @@ -1454,8 +1456,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t scratch_release(target, &scratch); return ERROR_FAIL; } - } else if (riscv_supports_extension(target, - riscv_current_hartid(target), 'D')) { + } else if (riscv_supports_extension(target, 'D')) { riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0)); } else { riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0)); @@ -1498,7 +1499,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t return result; } -int wait_for_authbusy(struct target *target, uint32_t *dmstatus) +static int wait_for_authbusy(struct target *target, uint32_t *dmstatus) { time_t start = time(NULL); while (1) { @@ -1544,7 +1545,7 @@ static int set_haltgroup(struct target *target, bool *supported) return ERROR_OK; } -static int discover_vlenb(struct target *target, int hartid) +static int discover_vlenb(struct target *target) { RISCV_INFO(r); riscv_reg_t vlenb; @@ -1552,12 +1553,12 @@ static int discover_vlenb(struct target *target, int hartid) 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; + r->vlenb = 0; return ERROR_OK; } - r->vlenb[hartid] = vlenb; + r->vlenb = vlenb; - LOG_INFO("hart %d: Vector support with vlenb=%d", hartid, r->vlenb[hartid]); + LOG_INFO("Vector support with vlenb=%d", r->vlenb); return ERROR_OK; } @@ -1705,6 +1706,8 @@ static int examine(struct target *target) LOG_DEBUG("Detected %d harts.", dm->hart_count); } + r->current_hartid = target->coreid; + if (dm->hart_count == 0) { LOG_ERROR("No harts found!"); return ERROR_FAIL; @@ -1712,55 +1715,50 @@ static int examine(struct target *target) /* Don't call any riscv_* functions until after we've counted the number of * cores and initialized registers. */ - for (int i = 0; i < dm->hart_count; ++i) { - if (!riscv_rtos_enabled(target) && i != target->coreid) - continue; - r->current_hartid = i; - if (riscv013_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; + if (riscv013_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; - bool halted = riscv_is_halted(target); - if (!halted) { - if (riscv013_halt_go(target) != ERROR_OK) { - LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i); - return ERROR_FAIL; - } - } - - /* Without knowing anything else we can at least mess with the - * program buffer. */ - r->debug_buffer_size[i] = info->progbufsize; - - int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64); - if (result == ERROR_OK) - r->xlen[i] = 64; - else - r->xlen[i] = 32; - - if (register_read(target, &r->misa[i], GDB_REGNO_MISA)) { - LOG_ERROR("Fatal: Failed to read MISA from hart %d.", i); + bool halted = riscv_is_halted(target); + if (!halted) { + if (riscv013_halt_go(target) != ERROR_OK) { + LOG_ERROR("Fatal: Hart %d failed to halt during examine()", r->current_hartid); 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; - - /* Display this as early as possible to help people who are using - * really slow simulators. */ - LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i], - r->misa[i]); - - if (!halted) - riscv013_step_or_resume_current_hart(target, false, false); } + /* Without knowing anything else we can at least mess with the + * program buffer. */ + r->debug_buffer_size = info->progbufsize; + + int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64); + if (result == ERROR_OK) + r->xlen = 64; + else + r->xlen = 32; + + if (register_read(target, &r->misa, GDB_REGNO_MISA)) { + LOG_ERROR("Fatal: Failed to read MISA from hart %d.", r->current_hartid); + return ERROR_FAIL; + } + + if (riscv_supports_extension(target, 'V')) { + if (discover_vlenb(target) != ERROR_OK) + return ERROR_FAIL; + } + + /* Now init registers based on what we discovered. */ + if (riscv_init_registers(target) != ERROR_OK) + return ERROR_FAIL; + + /* Display this as early as possible to help people who are using + * really slow simulators. */ + LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen, + r->misa); + + if (!halted) + riscv013_step_or_resume_current_hart(target, false, false); + target_set_examined(target); if (target->smp) { @@ -1780,27 +1778,31 @@ static int examine(struct target *target) * We will need to update those suites if we want to change that text. */ LOG_INFO("Examined RISC-V core; found %d harts", riscv_count_harts(target)); - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (riscv_hart_enabled(target, i)) { - LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i], - r->misa[i]); - } else { - LOG_INFO(" hart %d: currently disabled", i); - } - } + LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen, + r->misa); return ERROR_OK; } -int riscv013_authdata_read(struct target *target, uint32_t *value) +static int riscv013_authdata_read(struct target *target, uint32_t *value, unsigned int index) { + if (index > 0) { + LOG_ERROR("Spec 0.13 only has a single authdata register."); + return ERROR_FAIL; + } + if (wait_for_authbusy(target, NULL) != ERROR_OK) return ERROR_FAIL; return dmi_read(target, value, DM_AUTHDATA); } -int riscv013_authdata_write(struct target *target, uint32_t value) +static int riscv013_authdata_write(struct target *target, uint32_t value, unsigned int index) { + if (index > 0) { + LOG_ERROR("Spec 0.13 only has a single authdata register."); + return ERROR_FAIL; + } + uint32_t before, after; if (wait_for_authbusy(target, &before) != ERROR_OK) return ERROR_FAIL; @@ -1835,27 +1837,74 @@ static int riscv013_hart_count(struct target *target) return dm->hart_count; } +/* Try to find out the widest memory access size depending on the selected memory access methods. */ 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); + RISCV_INFO(r); - 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; + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { + int method = r->mem_access_methods[i]; - return riscv_xlen(target); + if (method == RISCV_MEM_ACCESS_PROGBUF) { + if (has_sufficient_progbuf(target, 3)) + return riscv_xlen(target); + } else if (method == RISCV_MEM_ACCESS_SYSBUS) { + 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; + } else if (method == RISCV_MEM_ACCESS_ABSTRACT) { + /* 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. */ + return riscv_xlen(target); + } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED) + /* No further mem access method to try. */ + break; + } + LOG_ERROR("Unable to determine supported data bits on this target. Assuming 32 bits."); + return 32; +} + +COMMAND_HELPER(riscv013_print_info, struct target *target) +{ + RISCV013_INFO(info); + + /* Abstract description. */ + riscv_print_info_line(CMD, "target", "memory.read_while_running8", get_field(info->sbcs, DM_SBCS_SBACCESS8)); + riscv_print_info_line(CMD, "target", "memory.write_while_running8", get_field(info->sbcs, DM_SBCS_SBACCESS8)); + riscv_print_info_line(CMD, "target", "memory.read_while_running16", get_field(info->sbcs, DM_SBCS_SBACCESS16)); + riscv_print_info_line(CMD, "target", "memory.write_while_running16", get_field(info->sbcs, DM_SBCS_SBACCESS16)); + riscv_print_info_line(CMD, "target", "memory.read_while_running32", get_field(info->sbcs, DM_SBCS_SBACCESS32)); + riscv_print_info_line(CMD, "target", "memory.write_while_running32", get_field(info->sbcs, DM_SBCS_SBACCESS32)); + riscv_print_info_line(CMD, "target", "memory.read_while_running64", get_field(info->sbcs, DM_SBCS_SBACCESS64)); + riscv_print_info_line(CMD, "target", "memory.write_while_running64", get_field(info->sbcs, DM_SBCS_SBACCESS64)); + riscv_print_info_line(CMD, "target", "memory.read_while_running128", get_field(info->sbcs, DM_SBCS_SBACCESS128)); + riscv_print_info_line(CMD, "target", "memory.write_while_running128", get_field(info->sbcs, DM_SBCS_SBACCESS128)); + + /* Lower level description. */ + riscv_print_info_line(CMD, "dm", "abits", info->abits); + riscv_print_info_line(CMD, "dm", "progbufsize", info->progbufsize); + riscv_print_info_line(CMD, "dm", "sbversion", get_field(info->sbcs, DM_SBCS_SBVERSION)); + riscv_print_info_line(CMD, "dm", "sbasize", get_field(info->sbcs, DM_SBCS_SBASIZE)); + riscv_print_info_line(CMD, "dm", "sbaccess128", get_field(info->sbcs, DM_SBCS_SBACCESS128)); + riscv_print_info_line(CMD, "dm", "sbaccess64", get_field(info->sbcs, DM_SBCS_SBACCESS64)); + riscv_print_info_line(CMD, "dm", "sbaccess32", get_field(info->sbcs, DM_SBCS_SBACCESS32)); + riscv_print_info_line(CMD, "dm", "sbaccess16", get_field(info->sbcs, DM_SBCS_SBACCESS16)); + riscv_print_info_line(CMD, "dm", "sbaccess8", get_field(info->sbcs, DM_SBCS_SBACCESS8)); + + uint32_t dmstatus; + if (dmstatus_read(target, &dmstatus, false) == ERROR_OK) + riscv_print_info_line(CMD, "dm", "authenticated", get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED)); + + return 0; } static int prep_for_vector_access(struct target *target, uint64_t *vtype, @@ -1885,8 +1934,7 @@ static int prep_for_vector_access(struct target *target, uint64_t *vtype, 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)); + *debug_vl = DIV_ROUND_UP(r->vlenb * 8, riscv_xlen(target)); if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK) return ERROR_FAIL; @@ -1909,6 +1957,9 @@ static int riscv013_get_register_buf(struct target *target, { assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + riscv_reg_t s0; if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; @@ -1965,6 +2016,9 @@ static int riscv013_set_register_buf(struct target *target, { assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + riscv_reg_t s0; if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) return ERROR_FAIL; @@ -2005,11 +2059,227 @@ static int riscv013_set_register_buf(struct target *target, return result; } +static uint32_t sb_sbaccess(unsigned int size_bytes) +{ + switch (size_bytes) { + case 1: + return set_field(0, DM_SBCS_SBACCESS, 0); + case 2: + return set_field(0, DM_SBCS_SBACCESS, 1); + case 4: + return set_field(0, DM_SBCS_SBACCESS, 2); + case 8: + return set_field(0, DM_SBCS_SBACCESS, 3); + case 16: + return set_field(0, DM_SBCS_SBACCESS, 4); + } + assert(0); + return 0; +} + +static int sb_write_address(struct target *target, target_addr_t address, + bool ensure_success) +{ + RISCV013_INFO(info); + unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); + /* There currently is no support for >64-bit addresses in OpenOCD. */ + if (sbasize > 96) + dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS3, 0, false, false); + if (sbasize > 64) + dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS2, 0, false, false); + if (sbasize > 32) + dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS1, address >> 32, false, false); + return dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS0, address, + false, ensure_success); +} + +static int batch_run(const struct target *target, struct riscv_batch *batch) +{ + RISCV013_INFO(info); + RISCV_INFO(r); + if (r->reset_delays_wait >= 0) { + r->reset_delays_wait -= batch->used_scans; + if (r->reset_delays_wait <= 0) { + batch->idle_count = 0; + info->dmi_busy_delay = 0; + info->ac_busy_delay = 0; + } + } + return riscv_batch_run(batch); +} + +static int sba_supports_access(struct target *target, unsigned int size_bytes) +{ + RISCV013_INFO(info); + switch (size_bytes) { + case 1: + return get_field(info->sbcs, DM_SBCS_SBACCESS8); + case 2: + return get_field(info->sbcs, DM_SBCS_SBACCESS16); + case 4: + return get_field(info->sbcs, DM_SBCS_SBACCESS32); + case 8: + return get_field(info->sbcs, DM_SBCS_SBACCESS64); + case 16: + return get_field(info->sbcs, DM_SBCS_SBACCESS128); + default: + return 0; + } +} + +static int sample_memory_bus_v1(struct target *target, + struct riscv_sample_buf *buf, + const riscv_sample_config_t *config, + int64_t until_ms) +{ + RISCV013_INFO(info); + unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); + if (sbasize > 64) { + LOG_ERROR("Memory sampling is only implemented for sbasize <= 64."); + return ERROR_NOT_IMPLEMENTED; + } + + if (get_field(info->sbcs, DM_SBCS_SBVERSION) != 1) { + LOG_ERROR("Memory sampling is only implemented for SBA version 1."); + return ERROR_NOT_IMPLEMENTED; + } + + uint32_t sbcs = 0; + uint32_t sbcs_valid = false; + + uint32_t sbaddress0 = 0; + bool sbaddress0_valid = false; + uint32_t sbaddress1 = 0; + bool sbaddress1_valid = false; + + /* How often to read each value in a batch. */ + const unsigned int repeat = 5; + + unsigned int enabled_count = 0; + for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) { + if (config->bucket[i].enabled) + enabled_count++; + } + + while (timeval_ms() < until_ms) { + /* + * batch_run() adds to the batch, so we can't simply reuse the same + * batch over and over. So we create a new one every time through the + * loop. + */ + struct riscv_batch *batch = riscv_batch_alloc( + target, 1 + enabled_count * 5 * repeat, + info->dmi_busy_delay + info->bus_master_read_delay); + if (!batch) + return ERROR_FAIL; + + unsigned int result_bytes = 0; + for (unsigned int n = 0; n < repeat; n++) { + for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) { + if (config->bucket[i].enabled) { + if (!sba_supports_access(target, config->bucket[i].size_bytes)) { + LOG_ERROR("Hardware does not support SBA access for %d-byte memory sampling.", + config->bucket[i].size_bytes); + return ERROR_NOT_IMPLEMENTED; + } + + uint32_t sbcs_write = DM_SBCS_SBREADONADDR; + if (enabled_count == 1) + sbcs_write |= DM_SBCS_SBREADONDATA; + sbcs_write |= sb_sbaccess(config->bucket[i].size_bytes); + if (!sbcs_valid || sbcs_write != sbcs) { + riscv_batch_add_dmi_write(batch, DM_SBCS, sbcs_write); + sbcs = sbcs_write; + sbcs_valid = true; + } + + if (sbasize > 32 && + (!sbaddress1_valid || + sbaddress1 != config->bucket[i].address >> 32)) { + sbaddress1 = config->bucket[i].address >> 32; + riscv_batch_add_dmi_write(batch, DM_SBADDRESS1, sbaddress1); + sbaddress1_valid = true; + } + if (!sbaddress0_valid || + sbaddress0 != (config->bucket[i].address & 0xffffffff)) { + sbaddress0 = config->bucket[i].address; + riscv_batch_add_dmi_write(batch, DM_SBADDRESS0, sbaddress0); + sbaddress0_valid = true; + } + if (config->bucket[i].size_bytes > 4) + riscv_batch_add_dmi_read(batch, DM_SBDATA1); + riscv_batch_add_dmi_read(batch, DM_SBDATA0); + result_bytes += 1 + config->bucket[i].size_bytes; + } + } + } + + if (buf->used + result_bytes >= buf->size) { + riscv_batch_free(batch); + break; + } + + size_t sbcs_key = riscv_batch_add_dmi_read(batch, DM_SBCS); + + int result = batch_run(target, batch); + if (result != ERROR_OK) + return result; + + uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_key); + if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { + /* Discard this batch (too much hassle to try to recover partial + * data) and try again with a larger delay. */ + info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1; + dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); + riscv_batch_free(batch); + continue; + } + if (get_field(sbcs_read, DM_SBCS_SBERROR)) { + /* The memory we're sampling was unreadable, somehow. Give up. */ + dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); + riscv_batch_free(batch); + return ERROR_FAIL; + } + + unsigned int read = 0; + for (unsigned int n = 0; n < repeat; n++) { + for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) { + if (config->bucket[i].enabled) { + assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE); + uint64_t value = 0; + if (config->bucket[i].size_bytes > 4) + value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read++)) << 32; + value |= riscv_batch_get_dmi_read_data(batch, read++); + + buf->buf[buf->used] = i; + buf_set_u64(buf->buf + buf->used + 1, 0, config->bucket[i].size_bytes * 8, value); + buf->used += 1 + config->bucket[i].size_bytes; + } + } + } + + riscv_batch_free(batch); + } + + return ERROR_OK; +} + +static int sample_memory(struct target *target, + struct riscv_sample_buf *buf, + riscv_sample_config_t *config, + int64_t until_ms) +{ + if (!config->enabled) + return ERROR_OK; + + return sample_memory_bus_v1(target, buf, config, until_ms); +} + 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; + RISCV_INFO(generic_info); generic_info->get_register = &riscv013_get_register; generic_info->set_register = &riscv013_set_register; @@ -2038,12 +2308,13 @@ static int init_target(struct command_context *cmd_ctx, 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->print_info = &riscv013_print_info; generic_info->version_specific = calloc(1, sizeof(riscv013_info_t)); if (!generic_info->version_specific) return ERROR_FAIL; + generic_info->sample_memory = sample_memory; riscv013_info_t *info = get_info(target); info->progbufsize = -1; @@ -2063,6 +2334,8 @@ static int init_target(struct command_context *cmd_ctx, info->abstract_read_fpr_supported = true; info->abstract_write_fpr_supported = true; + info->has_aampostincrement = YNM_MAYBE; + return ERROR_OK; } @@ -2074,7 +2347,10 @@ static int assert_reset(struct target *target) uint32_t control_base = set_field(0, DM_DMCONTROL_DMACTIVE, 1); - if (target->rtos) { + if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) { + /* Run the user-supplied script if there is one. */ + target_handle_event(target, TARGET_EVENT_RESET_ASSERT); + } else if (target->rtos) { /* There's only one target, and OpenOCD thinks each hart is a thread. * We must reset them all. */ @@ -2082,15 +2358,12 @@ static int assert_reset(struct target *target) /* Set haltreq for each hart. */ uint32_t control = control_base; - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; - control = set_hartsel(control_base, i); - control = set_field(control, DM_DMCONTROL_HALTREQ, - target->reset_halt ? 1 : 0); - dmi_write(target, DM_DMCONTROL, control); - } + control = set_hartsel(control_base, target->coreid); + control = set_field(control, DM_DMCONTROL_HALTREQ, + target->reset_halt ? 1 : 0); + dmi_write(target, DM_DMCONTROL, control); + /* Assert ndmreset */ control = set_field(control, DM_DMCONTROL_NDMRESET, 1); dmi_write(target, DM_DMCONTROL, control); @@ -2138,7 +2411,7 @@ static int deassert_reset(struct target *target) for (int i = 0; i < riscv_count_harts(target); ++i) { int index = i; if (target->rtos) { - if (!riscv_hart_enabled(target, index)) + if (index != target->coreid) continue; dmi_write(target, DM_DMCONTROL, set_hartsel(control, index)); @@ -2146,16 +2419,7 @@ static int deassert_reset(struct target *target) index = r->current_hartid; } - char *operation; - uint32_t expected_field; - if (target->reset_halt) { - operation = "halt"; - expected_field = DM_DMSTATUS_ALLHALTED; - } else { - operation = "run"; - expected_field = DM_DMSTATUS_ALLRUNNING; - } - LOG_DEBUG("Waiting for hart %d to %s out of reset.", index, operation); + LOG_DEBUG("Waiting for hart %d to come out of reset.", index); while (1) { int result = dmstatus_read_timeout(target, &dmstatus, true, riscv_reset_timeout_sec); @@ -2166,13 +2430,20 @@ static int deassert_reset(struct target *target) index, riscv_reset_timeout_sec); if (result != ERROR_OK) return result; - if (get_field(dmstatus, expected_field)) + /* Certain debug modules, like the one in GD32VF103 + * MCUs, violate the specification's requirement that + * each hart is in "exactly one of four states" and, + * during reset, report harts as both unavailable and + * halted/running. To work around this, we check for + * the absence of the unavailable state rather than + * the presence of any other state. */ + if (!get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) break; if (time(NULL) - start > riscv_reset_timeout_sec) { - LOG_ERROR("Hart %d didn't %s coming out of reset in %ds; " + LOG_ERROR("Hart %d didn't leave reset in %ds; " "dmstatus=0x%x; " "Increase the timeout with riscv set_reset_timeout_sec.", - index, operation, riscv_reset_timeout_sec, dmstatus); + index, riscv_reset_timeout_sec, dmstatus); return ERROR_FAIL; } } @@ -2194,8 +2465,6 @@ static int deassert_reset(struct target *target) static int execute_fence(struct target *target) { - int old_hartid = riscv_current_hartid(target); - /* FIXME: For non-coherent systems we need to flush the caches right * here, but there's no ISA-defined way of doing that. */ { @@ -2208,27 +2477,6 @@ static int execute_fence(struct target *target) LOG_DEBUG("Unable to execute pre-fence"); } - for (int i = 0; i < riscv_count_harts(target); ++i) { - 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; - riscv_program_init(&program, target); - riscv_program_fence_i(&program); - riscv_program_fence(&program); - int result = riscv_program_exec(&program, target); - if (result != ERROR_OK) - LOG_DEBUG("Unable to execute fence on hart %d", i); - } - - riscv_set_current_hartid(target, old_hartid); - return ERROR_OK; } @@ -2278,24 +2526,6 @@ static int read_memory_bus_word(struct target *target, target_addr_t address, return ERROR_OK; } -static uint32_t sb_sbaccess(unsigned size_bytes) -{ - switch (size_bytes) { - case 1: - return set_field(0, DM_SBCS_SBACCESS, 0); - case 2: - return set_field(0, DM_SBCS_SBACCESS, 1); - case 4: - return set_field(0, DM_SBCS_SBACCESS, 2); - case 8: - return set_field(0, DM_SBCS_SBACCESS, 3); - case 16: - return set_field(0, DM_SBCS_SBACCESS, 4); - } - assert(0); - return 0; /* Make mingw happy. */ -} - static target_addr_t sb_read_address(struct target *target) { RISCV013_INFO(info); @@ -2312,20 +2542,6 @@ static target_addr_t sb_read_address(struct target *target) return address; } -static int sb_write_address(struct target *target, target_addr_t address) -{ - RISCV013_INFO(info); - 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, DM_SBADDRESS3, 0); - if (sbasize > 64) - dmi_write(target, DM_SBADDRESS2, 0); - if (sbasize > 32) - 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); @@ -2453,6 +2669,10 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address, } } + uint32_t sbcs; + if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK) + return ERROR_FAIL; + return ERROR_OK; } @@ -2482,7 +2702,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, return ERROR_FAIL; /* This address write will trigger the first read. */ - if (sb_write_address(target, next_address) != ERROR_OK) + if (sb_write_address(target, next_address, true) != ERROR_OK) return ERROR_FAIL; if (info->bus_master_read_delay) { @@ -2509,6 +2729,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, next_read); return ERROR_FAIL; } + keep_alive(); dmi_status_t status = dmi_scan(target, NULL, &value, DMI_OP_READ, sbdata[j], 0, false); if (status == DMI_STATUS_BUSY) @@ -2570,7 +2791,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { /* We read while the target was busy. Slow down and try again. */ - if (dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR) != ERROR_OK) + if (dmi_write(target, DM_SBCS, sbcs_read | 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; @@ -2592,19 +2813,136 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address, return ERROR_OK; } -static int batch_run(const struct target *target, struct riscv_batch *batch) +static void log_mem_access_result(struct target *target, bool success, int method, bool read) { - RISCV013_INFO(info); RISCV_INFO(r); - if (r->reset_delays_wait >= 0) { - r->reset_delays_wait -= batch->used_scans; - if (r->reset_delays_wait <= 0) { - batch->idle_count = 0; - info->dmi_busy_delay = 0; - info->ac_busy_delay = 0; + bool warn = false; + char msg[60]; + + /* Compose the message */ + snprintf(msg, 60, "%s to %s memory via %s.", + success ? "Succeeded" : "Failed", + read ? "read" : "write", + (method == RISCV_MEM_ACCESS_PROGBUF) ? "program buffer" : + (method == RISCV_MEM_ACCESS_SYSBUS) ? "system bus" : "abstract access"); + + /* Determine the log message severity. Show warnings only once. */ + if (!success) { + if (method == RISCV_MEM_ACCESS_PROGBUF) { + warn = r->mem_access_progbuf_warn; + r->mem_access_progbuf_warn = false; + } + if (method == RISCV_MEM_ACCESS_SYSBUS) { + warn = r->mem_access_sysbus_warn; + r->mem_access_sysbus_warn = false; + } + if (method == RISCV_MEM_ACCESS_ABSTRACT) { + warn = r->mem_access_abstract_warn; + r->mem_access_abstract_warn = false; } } - return riscv_batch_run(batch); + + if (warn) + LOG_WARNING("%s", msg); + else + LOG_DEBUG("%s", msg); +} + +static bool mem_should_skip_progbuf(struct target *target, target_addr_t address, + uint32_t size, bool read, char **skip_reason) +{ + assert(skip_reason); + + if (!has_sufficient_progbuf(target, 3)) { + LOG_DEBUG("Skipping mem %s via progbuf - insufficient progbuf size.", + read ? "read" : "write"); + *skip_reason = "skipped (insufficient progbuf)"; + return true; + } + if (target->state != TARGET_HALTED) { + LOG_DEBUG("Skipping mem %s via progbuf - target not halted.", + read ? "read" : "write"); + *skip_reason = "skipped (target not halted)"; + return true; + } + if (riscv_xlen(target) < size * 8) { + LOG_DEBUG("Skipping mem %s via progbuf - XLEN (%d) is too short for %d-bit memory access.", + read ? "read" : "write", riscv_xlen(target), size * 8); + *skip_reason = "skipped (XLEN too short)"; + return true; + } + if (size > 8) { + LOG_DEBUG("Skipping mem %s via progbuf - unsupported size.", + read ? "read" : "write"); + *skip_reason = "skipped (unsupported size)"; + return true; + } + if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) { + LOG_DEBUG("Skipping mem %s via progbuf - progbuf only supports %u-bit address.", + read ? "read" : "write", riscv_xlen(target)); + *skip_reason = "skipped (too large address)"; + return true; + } + + return false; +} + +static bool mem_should_skip_sysbus(struct target *target, target_addr_t address, + uint32_t size, uint32_t increment, bool read, char **skip_reason) +{ + assert(skip_reason); + + RISCV013_INFO(info); + if (!sba_supports_access(target, size)) { + LOG_DEBUG("Skipping mem %s via system bus - unsupported size.", + read ? "read" : "write"); + *skip_reason = "skipped (unsupported size)"; + return true; + } + unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); + if ((sizeof(address) * 8 > sbasize) && (address >> sbasize)) { + LOG_DEBUG("Skipping mem %s via system bus - sba only supports %u-bit address.", + read ? "read" : "write", sbasize); + *skip_reason = "skipped (too large address)"; + return true; + } + if (read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) { + LOG_DEBUG("Skipping mem read via system bus - " + "sba reads only support size==increment or also size==0 for sba v1."); + *skip_reason = "skipped (unsupported increment)"; + return true; + } + + return false; +} + +static bool mem_should_skip_abstract(struct target *target, target_addr_t address, + uint32_t size, uint32_t increment, bool read, char **skip_reason) +{ + assert(skip_reason); + + if (size > 8) { + /* TODO: Add 128b support if it's ever used. Involves modifying + read/write_abstract_arg() to work on two 64b values. */ + LOG_DEBUG("Skipping mem %s via abstract access - unsupported size: %d bits", + read ? "read" : "write", size * 8); + *skip_reason = "skipped (unsupported size)"; + return true; + } + if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) { + LOG_DEBUG("Skipping mem %s via abstract access - abstract access only supports %u-bit address.", + read ? "read" : "write", riscv_xlen(target)); + *skip_reason = "skipped (too large address)"; + return true; + } + if (read && size != increment) { + LOG_ERROR("Skipping mem read via abstract access - " + "abstract command reads only support size==increment."); + *skip_reason = "skipped (unsupported increment)"; + return true; + } + + return false; } /* @@ -2615,12 +2953,10 @@ static int batch_run(const struct target *target, struct riscv_batch *batch) 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; - } + RISCV013_INFO(info); int result = ERROR_OK; + bool use_aampostincrement = info->has_aampostincrement != YNM_NO; LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, size, address); @@ -2629,25 +2965,19 @@ static int read_memory_abstract(struct target *target, target_addr_t 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", size); - return ERROR_FAIL; - } /* Create the command (physical address, postincrement, read) */ - uint32_t command = access_memory_command(target, false, width, true, false); + uint32_t command = access_memory_command(target, false, width, use_aampostincrement, false); /* Execute the reads */ uint8_t *p = buffer; bool updateaddr = true; - unsigned width32 = (width + 31) / 32 * 32; + unsigned int width32 = (width < 32) ? 32 : width; for (uint32_t c = 0; c < count; c++) { - /* Only update the address initially and let postincrement update it */ + /* Update the address if it is the first time or aampostincrement is not supported by the target. */ if (updateaddr) { /* Set arg1 to the address: address + c * size */ - result = write_abstract_arg(target, 1, address, riscv_xlen(target)); + result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target)); if (result != ERROR_OK) { LOG_ERROR("Failed to write arg1 during read_memory_abstract()."); return result; @@ -2656,16 +2986,38 @@ static int read_memory_abstract(struct target *target, target_addr_t address, /* Execute the command */ result = execute_abstract_command(target, command); - if (result != ERROR_OK) { - LOG_ERROR("Failed to execute command read_memory_abstract()."); - return result; + + if (info->has_aampostincrement == YNM_MAYBE) { + if (result == ERROR_OK) { + /* Safety: double-check that the address was really auto-incremented */ + riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target)); + if (new_address == address + size) { + LOG_DEBUG("aampostincrement is supported on this target."); + info->has_aampostincrement = YNM_YES; + } else { + LOG_WARNING("Buggy aampostincrement! Address not incremented correctly."); + info->has_aampostincrement = YNM_NO; + } + } else { + /* Try the same access but with postincrement disabled. */ + command = access_memory_command(target, false, width, false, false); + result = execute_abstract_command(target, command); + if (result == ERROR_OK) { + LOG_DEBUG("aampostincrement is not supported on this target."); + info->has_aampostincrement = YNM_NO; + } + } } + if (result != ERROR_OK) + 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; + if (info->has_aampostincrement == YNM_YES) + updateaddr = false; p += size; } @@ -2680,22 +3032,18 @@ static int read_memory_abstract(struct target *target, target_addr_t address, static int write_memory_abstract(struct target *target, target_addr_t address, uint32_t size, uint32_t count, const uint8_t *buffer) { + RISCV013_INFO(info); int result = ERROR_OK; + bool use_aampostincrement = info->has_aampostincrement != YNM_NO; 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); + uint32_t command = access_memory_command(target, false, width, use_aampostincrement, true); /* Execute the writes */ const uint8_t *p = buffer; @@ -2709,10 +3057,10 @@ static int write_memory_abstract(struct target *target, target_addr_t address, return result; } - /* Only update the address initially and let postincrement update it */ + /* Update the address if it is the first time or aampostincrement is not supported by the target. */ if (updateaddr) { /* Set arg1 to the address: address + c * size */ - result = write_abstract_arg(target, 1, address, riscv_xlen(target)); + result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target)); if (result != ERROR_OK) { LOG_ERROR("Failed to write arg1 during write_memory_abstract()."); return result; @@ -2721,12 +3069,34 @@ static int write_memory_abstract(struct target *target, target_addr_t address, /* Execute the command */ result = execute_abstract_command(target, command); - if (result != ERROR_OK) { - LOG_ERROR("Failed to execute command write_memory_abstract()."); - return result; + + if (info->has_aampostincrement == YNM_MAYBE) { + if (result == ERROR_OK) { + /* Safety: double-check that the address was really auto-incremented */ + riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target)); + if (new_address == address + size) { + LOG_DEBUG("aampostincrement is supported on this target."); + info->has_aampostincrement = YNM_YES; + } else { + LOG_WARNING("Buggy aampostincrement! Address not incremented correctly."); + info->has_aampostincrement = YNM_NO; + } + } else { + /* Try the same access but with postincrement disabled. */ + command = access_memory_command(target, false, width, false, true); + result = execute_abstract_command(target, command); + if (result == ERROR_OK) { + LOG_DEBUG("aampostincrement is not supported on this target."); + info->has_aampostincrement = YNM_NO; + } + } } - updateaddr = false; + if (result != ERROR_OK) + return result; + + if (info->has_aampostincrement == YNM_YES) + updateaddr = false; p += size; } @@ -2982,9 +3352,10 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address, return ERROR_FAIL; uint64_t s0; + int result = ERROR_FAIL; if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK) - return ERROR_FAIL; + goto restore_mstatus; /* Write the program (load, increment) */ struct riscv_program program; @@ -3006,40 +3377,42 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address, break; default: LOG_ERROR("Unsupported size: %d", size); - return ERROR_FAIL; + goto restore_mstatus; } if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); if (riscv_program_ebreak(&program) != ERROR_OK) - return ERROR_FAIL; + goto restore_mstatus; if (riscv_program_write(&program) != ERROR_OK) - return ERROR_FAIL; + goto restore_mstatus; /* Write address to S0, and execute buffer. */ if (write_abstract_arg(target, 0, address, riscv_xlen(target)) != ERROR_OK) - return ERROR_FAIL; + goto restore_mstatus; uint32_t command = access_register_command(target, GDB_REGNO_S0, riscv_xlen(target), AC_ACCESS_REGISTER_WRITE | AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC); if (execute_abstract_command(target, command) != ERROR_OK) - return ERROR_FAIL; + goto restore_s0; uint64_t value; if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK) - return ERROR_FAIL; + goto restore_s0; buf_set_u64(buffer, 0, 8 * size, value); log_memory_access(address, value, size, true); + result = ERROR_OK; +restore_s0: if (riscv_set_register(target, GDB_REGNO_S0, s0) != ERROR_OK) - return ERROR_FAIL; + result = ERROR_FAIL; - /* Restore MSTATUS */ +restore_mstatus: if (mstatus != mstatus_old) if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) - return ERROR_FAIL; + result = ERROR_FAIL; - return ERROR_OK; + return result; } /** @@ -3074,8 +3447,8 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, 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 + /* s0 holds the next address to read from + * s1 holds the next data value read * s2 is a counter in case increment is 0 */ uint64_t s0, s1, s2; @@ -3083,7 +3456,7 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, return ERROR_FAIL; if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK) return ERROR_FAIL; - if (increment == 0 && register_read(target, &s2, GDB_REGNO_S1) != ERROR_OK) + if (increment == 0 && register_read(target, &s2, GDB_REGNO_S2) != ERROR_OK) return ERROR_FAIL; /* Write the program (load, increment) */ @@ -3133,7 +3506,6 @@ static int read_memory_progbuf(struct target *target, target_addr_t address, uint8_t *buffer_i = buffer; 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, count_i, buffer_i, increment); @@ -3168,30 +3540,62 @@ static int read_memory(struct target *target, target_addr_t address, if (count == 0) return ERROR_OK; - 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 (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) { + LOG_ERROR("BUG: Unsupported size for memory read: %d", size); + return ERROR_FAIL; } - if (has_sufficient_progbuf(target, 3)) - return read_memory_progbuf(target, address, size, count, buffer, - increment); + int ret = ERROR_FAIL; + RISCV_INFO(r); + RISCV013_INFO(info); - return read_memory_abstract(target, address, size, count, buffer, - increment); + char *progbuf_result = "disabled"; + char *sysbus_result = "disabled"; + char *abstract_result = "disabled"; + + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { + int method = r->mem_access_methods[i]; + + if (method == RISCV_MEM_ACCESS_PROGBUF) { + if (mem_should_skip_progbuf(target, address, size, true, &progbuf_result)) + continue; + + ret = read_memory_progbuf(target, address, size, count, buffer, increment); + + if (ret != ERROR_OK) + progbuf_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_SYSBUS) { + if (mem_should_skip_sysbus(target, address, size, increment, true, &sysbus_result)) + continue; + + if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0) + ret = read_memory_bus_v0(target, address, size, count, buffer, increment); + else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1) + ret = read_memory_bus_v1(target, address, size, count, buffer, increment); + + if (ret != ERROR_OK) + sysbus_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_ABSTRACT) { + if (mem_should_skip_abstract(target, address, size, increment, true, &abstract_result)) + continue; + + ret = read_memory_abstract(target, address, size, count, buffer, increment); + + if (ret != ERROR_OK) + abstract_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED) + /* No further mem access method to try. */ + break; + + log_mem_access_result(target, ret == ERROR_OK, method, true); + + if (ret == ERROR_OK) + return ret; + } + + LOG_ERROR("Target %s: Failed to read memory (addr=0x%" PRIx64 ")", target_name(target), address); + LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); + return ret; } static int write_memory_bus_v0(struct target *target, target_addr_t address, @@ -3260,7 +3664,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, int result; - sb_write_address(target, next_address); + sb_write_address(target, next_address, true); while (next_address < end_address) { LOG_DEBUG("transferring burst starting at address 0x%" TARGET_PRIxADDR, next_address); @@ -3310,54 +3714,76 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, next_address += size; } + /* Execute the batch of writes */ result = batch_run(target, batch); riscv_batch_free(batch); if (result != ERROR_OK) return result; + /* Read sbcs value. + * At the same time, detect if DMI busy has occurred during the batch write. */ bool dmi_busy_encountered; if (dmi_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ, - DM_SBCS, 0, false, false) != ERROR_OK) + DM_SBCS, 0, false, true) != ERROR_OK) return ERROR_FAIL; + if (dmi_busy_encountered) + LOG_DEBUG("DMI busy encountered during system bus write."); + /* Wait until sbbusy goes low */ time_t start = time(NULL); - bool dmi_busy = dmi_busy_encountered; - while (get_field(sbcs, DM_SBCS_SBBUSY) || dmi_busy) { + while (get_field(sbcs, DM_SBCS_SBBUSY)) { if (time(NULL) - start > riscv_command_timeout_sec) { LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). " - "Increase the timeout with riscv set_command_timeout_sec.", - riscv_command_timeout_sec, sbcs); + "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) + if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK) return ERROR_FAIL; } if (get_field(sbcs, DM_SBCS_SBBUSYERROR)) { - /* We wrote while the target was busy. Slow down and try again. */ - dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR); + /* We wrote while the target was busy. */ + LOG_DEBUG("Sbbusyerror encountered during system bus write."); + /* Clear the sticky error flag. */ + dmi_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR); + /* Slow down before trying again. */ info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1; } if (get_field(sbcs, DM_SBCS_SBBUSYERROR) || dmi_busy_encountered) { + /* Recover from the case when the write commands were issued too fast. + * Determine the address from which to resume writing. */ next_address = sb_read_address(target); if (next_address < address) { /* This should never happen, probably buggy hardware. */ - LOG_DEBUG("unexpected system bus address 0x%" TARGET_PRIxADDR, - next_address); + LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR + " - buggy sbautoincrement in hw?", next_address); + /* Fail the whole operation. */ return ERROR_FAIL; } - + /* Try again - resume writing. */ continue; } - 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. */ + unsigned int sberror = get_field(sbcs, DM_SBCS_SBERROR); + if (sberror != 0) { + /* Sberror indicates the bus access failed, but not because we issued the writes + * too fast. Cannot recover. Sbaddress holds the address where the error occurred + * (unless sbautoincrement in the HW is buggy). + */ + target_addr_t sbaddress = sb_read_address(target); + LOG_DEBUG("System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")", + sberror, sbaddress); + if (sbaddress < address) { + /* This should never happen, probably buggy hardware. + * Make a note to the user not to trust the sbaddress value. */ + LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR + " - buggy sbautoincrement in hw?", next_address); + } + /* Clear the sticky error flag */ dmi_write(target, DM_SBCS, DM_SBCS_SBERROR); + /* Fail the whole operation */ return ERROR_FAIL; } } @@ -3560,26 +3986,62 @@ error: 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 (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba) - return write_memory_progbuf(target, address, size, count, buffer); - - 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, DM_SBCS_SBVERSION) == 1) - return write_memory_bus_v1(target, address, size, count, buffer); + if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) { + LOG_ERROR("BUG: Unsupported size for memory write: %d", size); + return ERROR_FAIL; } - if (has_sufficient_progbuf(target, 3)) - return write_memory_progbuf(target, address, size, count, buffer); + int ret = ERROR_FAIL; + RISCV_INFO(r); + RISCV013_INFO(info); - return write_memory_abstract(target, address, size, count, buffer); + char *progbuf_result = "disabled"; + char *sysbus_result = "disabled"; + char *abstract_result = "disabled"; + + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { + int method = r->mem_access_methods[i]; + + if (method == RISCV_MEM_ACCESS_PROGBUF) { + if (mem_should_skip_progbuf(target, address, size, false, &progbuf_result)) + continue; + + ret = write_memory_progbuf(target, address, size, count, buffer); + + if (ret != ERROR_OK) + progbuf_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_SYSBUS) { + if (mem_should_skip_sysbus(target, address, size, 0, false, &sysbus_result)) + continue; + + if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0) + ret = write_memory_bus_v0(target, address, size, count, buffer); + else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1) + ret = write_memory_bus_v1(target, address, size, count, buffer); + + if (ret != ERROR_OK) + sysbus_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_ABSTRACT) { + if (mem_should_skip_abstract(target, address, size, 0, false, &abstract_result)) + continue; + + ret = write_memory_abstract(target, address, size, count, buffer); + + if (ret != ERROR_OK) + abstract_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED) + /* No further mem access method to try. */ + break; + + log_mem_access_result(target, ret == ERROR_OK, method, false); + + if (ret == ERROR_OK) + return ret; + } + + LOG_ERROR("Target %s: Failed to write memory (addr=0x%" PRIx64 ")", target_name(target), address); + LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); + return ret; } static int arch_state(struct target *target) @@ -3603,17 +4065,18 @@ struct target_type riscv013_target = { .write_memory = write_memory, - .arch_state = arch_state, + .arch_state = arch_state }; /*** 0.13-specific implementations of various RISC-V helper functions. ***/ static int riscv013_get_register(struct target *target, - riscv_reg_t *value, int hid, int rid) + riscv_reg_t *value, int rid) { - LOG_DEBUG("[%d] reading register %s on hart %d", target->coreid, - gdb_regno_name(rid), hid); + LOG_DEBUG("[%s] reading register %s", target_name(target), + gdb_regno_name(rid)); - riscv_set_current_hartid(target, hid); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; int result = ERROR_OK; if (rid == GDB_REGNO_PC) { @@ -3624,7 +4087,8 @@ static int riscv013_get_register(struct target *target, uint64_t dcsr; /* TODO: move this into riscv.c. */ result = register_read(target, &dcsr, GDB_REGNO_DCSR); - *value = get_field(dcsr, CSR_DCSR_PRV); + *value = set_field(0, VIRT_PRIV_V, get_field(dcsr, CSR_DCSR_V)); + *value = set_field(*value, VIRT_PRIV_PRV, get_field(dcsr, CSR_DCSR_PRV)); } else { result = register_read(target, value, rid); if (result != ERROR_OK) @@ -3634,12 +4098,11 @@ static int riscv013_get_register(struct target *target, return result; } -static int riscv013_set_register(struct target *target, int hid, int rid, uint64_t value) +static int riscv013_set_register(struct target *target, int rid, uint64_t value) { - 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); + riscv013_select_current_hart(target); + LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s", + target->coreid, value, gdb_regno_name(rid)); if (rid <= GDB_REGNO_XPR31) { return register_write_direct(target, rid, value); @@ -3657,7 +4120,8 @@ static int riscv013_set_register(struct target *target, int hid, int rid, uint64 } else if (rid == GDB_REGNO_PRIV) { uint64_t dcsr; register_read(target, &dcsr, GDB_REGNO_DCSR); - dcsr = set_field(dcsr, CSR_DCSR_PRV, value); + dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV)); + dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V)); return register_write_direct(target, GDB_REGNO_DCSR, dcsr); } else { return register_write_direct(target, rid, value); @@ -3748,10 +4212,8 @@ static int riscv013_halt_prep(struct target *target) 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; - } + if (select_prepped_harts(target, &use_hasel) != ERROR_OK) + return ERROR_FAIL; RISCV_INFO(r); LOG_DEBUG("halting hart %d", r->current_hartid); @@ -3802,10 +4264,8 @@ static int riscv013_halt_go(struct target *target) static int riscv013_resume_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; - } + if (select_prepped_harts(target, &use_hasel) != ERROR_OK) + return ERROR_FAIL; return riscv013_step_or_resume_current_hart(target, false, use_hasel); } @@ -3865,6 +4325,8 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target) if (result != ERROR_OK) return RISCV_HALT_UNKNOWN; + LOG_DEBUG("dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE)); + switch (get_field(dcsr, CSR_DCSR_CAUSE)) { case CSR_DCSR_CAUSE_SWBP: return RISCV_HALT_BREAKPOINT; @@ -3884,7 +4346,7 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target) return RISCV_HALT_GROUP; } - LOG_ERROR("Unknown DCSR cause field: %x", (int)get_field(dcsr, CSR_DCSR_CAUSE)); + LOG_ERROR("Unknown DCSR cause field: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE)); LOG_ERROR(" dcsr=0x%016lx", (long)dcsr); return RISCV_HALT_UNKNOWN; } @@ -4400,477 +4862,3 @@ void riscv013_clear_abstract_error(struct target *target) /* Clear the error status. */ 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 ? __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", (pass) ? "PASSED" : "FAILED"); \ - assert(pass); \ - total_tests++; \ -} - -#define COMPLIANCE_MUST_PASS(b) COMPLIANCE_TEST(ERROR_OK == (b), "Regular calls must return ERROR_OK") - -#define COMPLIANCE_READ(target, addr, value) COMPLIANCE_MUST_PASS(dmi_read(target, addr, value)) -#define COMPLIANCE_WRITE(target, addr, value) COMPLIANCE_MUST_PASS(dmi_write(target, addr, value)) - -#define COMPLIANCE_CHECK_RO(target, addr) \ -{ \ - uint32_t orig; \ - uint32_t inverse; \ - COMPLIANCE_READ(target, &orig, addr); \ - COMPLIANCE_WRITE(target, addr, ~orig); \ - COMPLIANCE_READ(target, &inverse, addr); \ - COMPLIANCE_TEST(orig == inverse, "Register must be read-only"); \ -} - -int riscv013_test_compliance(struct target *target) -{ - 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 = DM_DMCONTROL_DMACTIVE; - uint32_t dmcontrol; - uint32_t testvar; - uint32_t testvar_read; - riscv_reg_t value; - RISCV013_INFO(info); - - /* All the bits of HARTSEL are covered by the examine sequence. */ - - /* hartreset */ - /* This field is optional. Either we can read and write it to 1/0, - 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, 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, 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(target)); - /* This bit is not actually readable according to the spec, so nothing to check.*/ - - /* 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(target, true, 0, false, false, false)); - - /* Halt all harts again so the test can continue.*/ - 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, 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, DM_HARTINFO); - - /* $dscratch CSRs */ - uint32_t nscratch = get_field(hartinfo, DM_HARTINFO_NSCRATCH); - for (unsigned int d = 0; d < nscratch; d++) { - riscv_reg_t testval, testval_read; - /* 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 DSCRATCH0."); - struct riscv_program program32; - riscv_program_init(&program32, target); - 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 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 DSCRATCH0."); - if (riscv_xlen(target) > 32) { - COMPLIANCE_TEST(testval == testval_read, - "All DSCRATCH0 registers in HARTINFO must be R/W."); - } else { - COMPLIANCE_TEST(testval_read == (testval & 0xFFFFFFFF), - "All DSCRATCH0 registers in HARTINFO must be R/W."); - } - } - } - } - /* TODO: dataaccess */ - if (get_field(hartinfo, DM_HARTINFO_DATAACCESS)) { - /* TODO: Shadowed in memory map. */ - /* TODO: datasize */ - /* TODO: dataaddr */ - } else { - /* TODO: Shadowed in CSRs. */ - /* TODO: datasize */ - /* TODO: dataaddr */ - } - - } - - /* HALTSUM -- TODO: More than 32 harts. Would need to loop over this to set hartsel */ - /* TODO: HALTSUM2, HALTSUM3 */ - /* HALTSUM0 */ - uint32_t expected_haltsum0 = 0; - for (int i = 0; i < MIN(riscv_count_harts(target), 32); i++) - expected_haltsum0 |= (1 << i); - - 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, DM_HALTSUM0, 0xffffffff); - COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM0); - COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O"); - - 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 */ - uint32_t expected_haltsum1 = 0; - for (int i = 0; i < MIN(riscv_count_harts(target), 1024); i += 32) - expected_haltsum1 |= (1 << (i/32)); - - 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, DM_HALTSUM1, 0xffffffff); - COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1); - COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O"); - - 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 */ - - /* TODO: HAWINDOW */ - - /* ABSTRACTCS */ - - uint32_t 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, DM_ABSTRACTCS_DATACOUNT); i++) { - testvar = (i + 1) * 0x11111111; - if (invert) - testvar = ~testvar; - COMPLIANCE_WRITE(target, DM_DATA0 + i, testvar); - } - 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, 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, DM_ABSTRACTCS_PROGBUFSIZE); i++) { - testvar = (i + 1) * 0x11111111; - if (invert) - testvar = ~testvar; - COMPLIANCE_WRITE(target, DM_PROGBUF0 + i, testvar); - } - 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, DM_PROGBUF0 + i); - COMPLIANCE_TEST(testvar_read == testvar, "All reported PROGBUF words must be R/W"); - } - } - - /* TODO: Cause and clear all error types */ - - /* 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, 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, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); - - 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, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); - - /* Basic Abstract Commands */ - for (unsigned int i = 1; i < 32; i = i << 1) { - riscv_reg_t testval = i | ((i + 1ULL) << 32); - riscv_reg_t testval_read; - COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_ZERO + i, testval) == ERROR_OK, - "GPR Writes should be supported."); - COMPLIANCE_MUST_PASS(write_abstract_arg(target, 0, 0xDEADBEEFDEADBEEF, 64)); - COMPLIANCE_TEST(register_read_direct(target, &testval_read, GDB_REGNO_ZERO + i) == ERROR_OK, - "GPR Reads should be supported."); - if (riscv_xlen(target) > 32) { - /* Dummy comment to satisfy linter, since removing the branches here doesn't actually compile. */ - COMPLIANCE_TEST(testval == testval_read, "GPR Reads and writes should be supported."); - } else { - /* Dummy comment to satisfy linter, since removing the branches here doesn't actually compile. */ - COMPLIANCE_TEST((testval & 0xFFFFFFFF) == testval_read, "GPR Reads and writes should be supported."); - } - } - - /* ABSTRACTAUTO - See which bits are actually writable */ - COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF); - uint32_t abstractauto; - uint32_t busy; - 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. */ - if (info->progbufsize >= 3) { - - testvar = 0; - COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_S0, 0) == ERROR_OK, - "Need to be able to write S0 to test ABSTRACTAUTO"); - struct riscv_program program; - COMPLIANCE_MUST_PASS(riscv_program_init(&program, target)); - /* This is also testing that WFI() is a NOP during debug mode. */ - 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, DM_ABSTRACTAUTO, 0x0); - COMPLIANCE_MUST_PASS(riscv_program_exec(&program, target)); - testvar++; - 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, DM_DATA0 + i); - do { - 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, 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, DM_PROGBUF0 + i); - do { - 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, DM_ABSTRACTCS_PROGBUFSIZE), - "AUTOEXEC may be writable up to PROGBUFSIZE bits."); - testvar++; - } - } - - COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0); - COMPLIANCE_TEST(register_read_direct(target, &value, GDB_REGNO_S0) == ERROR_OK, - "Need to be able to read S0 to test ABSTRACTAUTO"); - - COMPLIANCE_TEST(testvar == value, - "ABSTRACTAUTO should cause COMMAND to run the expected number of times."); - } - } - - /* Single-Step each hart. */ - for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) { - COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel)); - COMPLIANCE_MUST_PASS(riscv013_on_step(target)); - COMPLIANCE_MUST_PASS(riscv013_step_current_hart(target)); - COMPLIANCE_TEST(riscv_halt_reason(target, hartsel) == RISCV_HALT_SINGLESTEP, - "Single Step should result in SINGLESTEP"); - } - - /* Core Register Tests */ - uint64_t bogus_dpc = 0xdeadbeef; - for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) { - COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel)); - - /* DCSR Tests */ - COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0x0)); - COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR)); - COMPLIANCE_TEST(value != 0, "Not all bits in DCSR are writable by Debugger"); - COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0xFFFFFFFF)); - COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR)); - COMPLIANCE_TEST(value != 0, "At least some bits in DCSR must be 1"); - - /* DPC. Note that DPC is sign-extended. */ - riscv_reg_t dpcmask = 0xFFFFFFFCUL; - riscv_reg_t dpc; - - if (riscv_xlen(target) > 32) - dpcmask |= (0xFFFFFFFFULL << 32); - - if (riscv_supports_extension(target, riscv_current_hartid(target), 'C')) - dpcmask |= 0x2; - - COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, dpcmask)); - COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC)); - COMPLIANCE_TEST(dpcmask == dpc, - "DPC must be sign-extended to XLEN and writable to all-1s (except the least significant bits)"); - COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, 0)); - COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC)); - COMPLIANCE_TEST(dpc == 0, "DPC must be writable to 0."); - if (hartsel == 0) - bogus_dpc = dpc; /* For a later test step */ - } - - /* NDMRESET - Asserting non-debug module reset should not reset Debug Module state. - But it should reset Hart State, e.g. DPC should get a different value. - Also make sure that DCSR reports cause of 'HALT' even though previously we single-stepped. - */ - - /* Write some registers. They should not be impacted by ndmreset. */ - COMPLIANCE_WRITE(target, DM_COMMAND, 0xFFFFFFFF); - - for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) { - testvar = (i + 1) * 0x11111111; - COMPLIANCE_WRITE(target, DM_PROGBUF0 + i, testvar); - } - - for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) { - testvar = (i + 1) * 0x11111111; - COMPLIANCE_WRITE(target, DM_DATA0 + i, testvar); - } - - COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF); - COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO); - - /* Pulse reset. */ - target->reset_halt = true; - COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, 0)); - COMPLIANCE_TEST(assert_reset(target) == ERROR_OK, "Must be able to assert NDMRESET"); - COMPLIANCE_TEST(deassert_reset(target) == ERROR_OK, "Must be able to deassert NDMRESET"); - - /* Verify that most stuff is not affected by ndmreset. */ - 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, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR); - COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0); - - for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) { - testvar = (i + 1) * 0x11111111; - 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, DM_ABSTRACTCS_DATACOUNT); i++) { - testvar = (i + 1) * 0x11111111; - COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i); - COMPLIANCE_TEST(testvar_read == testvar, "DATA words must not be affected by NDMRESET"); - } - - /* Verify that DPC *is* affected by ndmreset. Since we don't know what it *should* be, - just verify that at least it's not the bogus value anymore. */ - - COMPLIANCE_TEST(bogus_dpc != 0xdeadbeef, "BOGUS DPC should have been set somehow (bug in compliance test)"); - COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DPC)); - COMPLIANCE_TEST(bogus_dpc != value, "NDMRESET should move DPC to reset value."); - - COMPLIANCE_TEST(riscv_halt_reason(target, 0) == RISCV_HALT_INTERRUPT, - "After NDMRESET halt, DCSR should report cause of halt"); - - /* DMACTIVE -- deasserting DMACTIVE should reset all the above values. */ - - /* Toggle dmactive */ - 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, 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, DM_ABSTRACTCS_DATACOUNT); i++) { - COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i); - COMPLIANCE_TEST(testvar_read == 0, "DATA words should reset to 0"); - } - - /* - * TODO: - * DCSR.cause priorities - * DCSR.stoptime/stopcycle - * DCSR.stepie - * DCSR.ebreak - * DCSR.prv - */ - - /* Halt every hart for any follow-up tests*/ - COMPLIANCE_MUST_PASS(riscv_halt(target)); - - uint32_t failed_tests = total_tests - passed_tests; - if (total_tests == passed_tests) { - LOG_INFO("ALL TESTS PASSED\n"); - return ERROR_OK; - } else { - LOG_INFO("%d TESTS FAILED\n", failed_tests); - return ERROR_FAIL; - } -} diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 07fb95550..7579e0f0c 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -19,6 +19,8 @@ #include "riscv.h" #include "gdb_regs.h" #include "rtos/rtos.h" +#include "debug_defines.h" +#include #define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) #define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) @@ -125,10 +127,10 @@ struct scan_field select_idcode = { bscan_tunnel_type_t bscan_tunnel_type; int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */ -static uint8_t bscan_zero[4] = {0}; -static uint8_t bscan_one[4] = {1}; +static const uint8_t bscan_zero[4] = {0}; +static const uint8_t bscan_one[4] = {1}; -uint8_t ir_user4[4] = {0x23}; +uint8_t ir_user4[4]; struct scan_field select_user4 = { .in_value = NULL, .out_value = ir_user4 @@ -202,7 +204,6 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC; /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC; -bool riscv_prefer_sba; bool riscv_enable_virt2phys = true; bool riscv_ebreakm = true; bool riscv_ebreaks = true; @@ -210,24 +211,12 @@ bool riscv_ebreaku = true; bool riscv_enable_virtual; -typedef struct { - uint16_t low, high; -} range_t; - -/* In addition to the ones in the standard spec, we'll also expose additional - * CSRs in this list. - * The list is either NULL, or a series of ranges (inclusive), terminated with - * 1,0. */ -range_t *expose_csr; -/* Same, but for custom registers. */ -range_t *expose_custom; - static enum { RO_NORMAL, RO_REVERSED } resume_order; -virt2phys_info_t sv32 = { +const virt2phys_info_t sv32 = { .name = "Sv32", .va_bits = 32, .level = 2, @@ -240,7 +229,7 @@ virt2phys_info_t sv32 = { .pa_ppn_mask = {0x3ff, 0xfff}, }; -virt2phys_info_t sv39 = { +const virt2phys_info_t sv39 = { .name = "Sv39", .va_bits = 39, .level = 3, @@ -253,7 +242,7 @@ virt2phys_info_t sv39 = { .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff}, }; -virt2phys_info_t sv48 = { +const virt2phys_info_t sv48 = { .name = "Sv48", .va_bits = 48, .level = 4, @@ -266,6 +255,22 @@ virt2phys_info_t sv48 = { .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff}, }; +void riscv_sample_buf_maybe_add_timestamp(struct target *target, bool before) +{ + RISCV_INFO(r); + uint32_t now = timeval_ms() & 0xffffffff; + if (r->sample_buf.used + 5 < r->sample_buf.size) { + if (before) + r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE; + else + r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_AFTER; + r->sample_buf.buf[r->sample_buf.used++] = now & 0xff; + r->sample_buf.buf[r->sample_buf.used++] = (now >> 8) & 0xff; + r->sample_buf.buf[r->sample_buf.used++] = (now >> 16) & 0xff; + r->sample_buf.buf[r->sample_buf.used++] = (now >> 24) & 0xff; + } +} + static int riscv_resume_go_all_harts(struct target *target); void select_dmi_via_bscan(struct target *target) @@ -363,8 +368,6 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out) return in; } - - static uint32_t dtmcontrol_scan(struct target *target, uint32_t out) { struct scan_field field; @@ -419,15 +422,23 @@ static struct target_type *get_target_type(struct target *target) } } +static int riscv_create_target(struct target *target, Jim_Interp *interp) +{ + LOG_DEBUG("riscv_create_target()"); + target->arch_info = calloc(1, sizeof(riscv_info_t)); + if (!target->arch_info) { + LOG_ERROR("Failed to allocate RISC-V target structure."); + return ERROR_FAIL; + } + riscv_info_init(target, target->arch_info); + return ERROR_OK; +} + static int riscv_init_target(struct command_context *cmd_ctx, struct target *target) { LOG_DEBUG("riscv_init_target()"); - target->arch_info = calloc(1, sizeof(riscv_info_t)); - if (!target->arch_info) - return ERROR_FAIL; - riscv_info_t *info = (riscv_info_t *) target->arch_info; - riscv_info_init(target, info); + RISCV_INFO(info); info->cmd_ctx = cmd_ctx; select_dtmcontrol.num_bits = target->tap->ir_length; @@ -435,6 +446,12 @@ static int riscv_init_target(struct command_context *cmd_ctx, select_idcode.num_bits = target->tap->ir_length; if (bscan_tunnel_ir_width != 0) { + assert(target->tap->ir_length >= 6); + uint32_t ir_user4_raw = 0x23 << (target->tap->ir_length - 6); + ir_user4[0] = (uint8_t)ir_user4_raw; + ir_user4[1] = (uint8_t)(ir_user4_raw >>= 8); + ir_user4[2] = (uint8_t)(ir_user4_raw >>= 8); + ir_user4[3] = (uint8_t)(ir_user4_raw >>= 8); select_user4.num_bits = target->tap->ir_length; bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width; if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) @@ -468,16 +485,29 @@ static void riscv_free_registers(struct target *target) static void riscv_deinit_target(struct target *target) { LOG_DEBUG("riscv_deinit_target()"); + + riscv_info_t *info = target->arch_info; struct target_type *tt = get_target_type(target); - if (tt) { + + if (tt && info->version_specific) tt->deinit_target(target); - riscv_info_t *info = (riscv_info_t *) target->arch_info; - free(info->reg_names); - free(info); - } riscv_free_registers(target); + range_list_t *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) { + free(entry->name); + free(entry); + } + + list_for_each_entry_safe(entry, tmp, &info->expose_custom, list) { + free(entry->name); + free(entry); + } + + free(info->reg_names); + free(target->arch_info); + target->arch_info = NULL; } @@ -494,7 +524,7 @@ static void trigger_from_breakpoint(struct trigger *trigger, trigger->unique_id = breakpoint->unique_id; } -static int maybe_add_trigger_t1(struct target *target, unsigned hartid, +static int maybe_add_trigger_t1(struct target *target, struct trigger *trigger, uint64_t tdata1) { RISCV_INFO(r); @@ -518,20 +548,19 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid, tdata1 = set_field(tdata1, bpcontrol_w, trigger->write); tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute); tdata1 = set_field(tdata1, bpcontrol_u, - !!(r->misa[hartid] & (1 << ('U' - 'A')))); + !!(r->misa & BIT('U' - 'A'))); tdata1 = set_field(tdata1, bpcontrol_s, - !!(r->misa[hartid] & (1 << ('S' - 'A')))); + !!(r->misa & BIT('S' - 'A'))); tdata1 = set_field(tdata1, bpcontrol_h, - !!(r->misa[hartid] & (1 << ('H' - 'A')))); + !!(r->misa & BIT('H' - 'A'))); tdata1 |= bpcontrol_m; tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */ tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */ - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1); + riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); riscv_reg_t tdata1_rb; - if (riscv_get_register_on_hart(target, &tdata1_rb, hartid, - GDB_REGNO_TDATA1) != ERROR_OK) + if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK) return ERROR_FAIL; LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb); @@ -539,16 +568,16 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid, LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%" PRIx64 " to tdata1 it contains 0x%" PRIx64, tdata1, tdata1_rb); - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); + riscv_set_register(target, GDB_REGNO_TDATA1, 0); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address); + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); return ERROR_OK; } -static int maybe_add_trigger_t2(struct target *target, unsigned hartid, +static int maybe_add_trigger_t2(struct target *target, struct trigger *trigger, uint64_t tdata1) { RISCV_INFO(r); @@ -565,11 +594,9 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, MCONTROL_ACTION_DEBUG_MODE); tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL); tdata1 |= MCONTROL_M; - if (r->misa[hartid] & (1 << ('H' - 'A'))) - tdata1 |= MCONTROL_H; - if (r->misa[hartid] & (1 << ('S' - 'A'))) + if (r->misa & (1 << ('S' - 'A'))) tdata1 |= MCONTROL_S; - if (r->misa[hartid] & (1 << ('U' - 'A'))) + if (r->misa & (1 << ('U' - 'A'))) tdata1 |= MCONTROL_U; if (trigger->execute) @@ -579,10 +606,10 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, if (trigger->write) tdata1 |= MCONTROL_STORE; - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1); + riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); uint64_t tdata1_rb; - int result = riscv_get_register_on_hart(target, &tdata1_rb, hartid, GDB_REGNO_TDATA1); + int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1); if (result != ERROR_OK) return result; LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb); @@ -591,11 +618,63 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%" PRIx64 " to tdata1 it contains 0x%" PRIx64, tdata1, tdata1_rb); - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); + riscv_set_register(target, GDB_REGNO_TDATA1, 0); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address); + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); + + return ERROR_OK; +} + +static int maybe_add_trigger_t6(struct target *target, + struct trigger *trigger, uint64_t tdata1) +{ + RISCV_INFO(r); + + /* tselect is already set */ + if (tdata1 & (CSR_MCONTROL6_EXECUTE | CSR_MCONTROL6_STORE | CSR_MCONTROL6_LOAD)) { + /* Trigger is already in use, presumably by user code. */ + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* address/data match trigger */ + tdata1 |= MCONTROL_DMODE(riscv_xlen(target)); + tdata1 = set_field(tdata1, CSR_MCONTROL6_ACTION, + MCONTROL_ACTION_DEBUG_MODE); + tdata1 = set_field(tdata1, CSR_MCONTROL6_MATCH, MCONTROL_MATCH_EQUAL); + tdata1 |= CSR_MCONTROL6_M; + if (r->misa & (1 << ('H' - 'A'))) + tdata1 |= CSR_MCONTROL6_VS | CSR_MCONTROL6_VU; + if (r->misa & (1 << ('S' - 'A'))) + tdata1 |= CSR_MCONTROL6_S; + if (r->misa & (1 << ('U' - 'A'))) + tdata1 |= CSR_MCONTROL6_U; + + if (trigger->execute) + tdata1 |= CSR_MCONTROL6_EXECUTE; + if (trigger->read) + tdata1 |= CSR_MCONTROL6_LOAD; + if (trigger->write) + tdata1 |= CSR_MCONTROL6_STORE; + + riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); + + uint64_t tdata1_rb; + int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1); + if (result != ERROR_OK) + return result; + LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb); + + if (tdata1 != tdata1_rb) { + LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%" + PRIx64 " to tdata1 it contains 0x%" PRIx64, + tdata1, tdata1_rb); + riscv_set_register(target, GDB_REGNO_TDATA1, 0); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); return ERROR_OK; } @@ -607,61 +686,36 @@ static int add_trigger(struct target *target, struct trigger *trigger) if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; - /* In RTOS mode, we need to set the same trigger in the same slot on every - * hart, to keep up the illusion that each hart is a thread running on the - * same core. */ - - /* Otherwise, we just set the trigger on the one hart this target deals - * with. */ - - riscv_reg_t tselect[RISCV_MAX_HARTS]; - - int first_hart = -1; - for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - if (first_hart < 0) - first_hart = hartid; - int result = riscv_get_register_on_hart(target, &tselect[hartid], - hartid, GDB_REGNO_TSELECT); - if (result != ERROR_OK) - return result; - } - assert(first_hart >= 0); + riscv_reg_t tselect; + if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; unsigned int i; - for (i = 0; i < r->trigger_count[first_hart]; i++) { + for (i = 0; i < r->trigger_count; i++) { if (r->trigger_unique_id[i] != -1) continue; - riscv_set_register_on_hart(target, first_hart, GDB_REGNO_TSELECT, i); + riscv_set_register(target, GDB_REGNO_TSELECT, i); uint64_t tdata1; - int result = riscv_get_register_on_hart(target, &tdata1, first_hart, - GDB_REGNO_TDATA1); + int result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1); if (result != ERROR_OK) return result; int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); result = ERROR_OK; - for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - if (hartid > first_hart) - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i); - switch (type) { - case 1: - result = maybe_add_trigger_t1(target, hartid, trigger, tdata1); - break; - case 2: - result = maybe_add_trigger_t2(target, hartid, trigger, tdata1); - break; - default: - LOG_DEBUG("trigger %d has unknown type %d", i, type); - continue; - } - - if (result != ERROR_OK) + switch (type) { + case 1: + result = maybe_add_trigger_t1(target, trigger, tdata1); + break; + case 2: + result = maybe_add_trigger_t2(target, trigger, tdata1); + break; + case 6: + result = maybe_add_trigger_t6(target, trigger, tdata1); + break; + default: + LOG_DEBUG("trigger %d has unknown type %d", i, type); continue; } @@ -674,14 +728,9 @@ static int add_trigger(struct target *target, struct trigger *trigger) break; } - for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, - tselect[hartid]); - } + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); - if (i >= r->trigger_count[first_hart]) { + if (i >= r->trigger_count) { LOG_ERROR("Couldn't find an available hardware trigger."); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -689,6 +738,124 @@ static int add_trigger(struct target *target, struct trigger *trigger) return ERROR_OK; } +/** + * Write one memory item of given "size". Use memory access of given "access_size". + * Utilize read-modify-write, if needed. + * */ +static int write_by_given_size(struct target *target, target_addr_t address, + uint32_t size, uint8_t *buffer, uint32_t access_size) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8); + + if (access_size <= size && address % access_size == 0) + /* Can do the memory access directly without a helper buffer. */ + return target_write_memory(target, address, access_size, size / access_size, buffer); + + unsigned int offset_head = address % access_size; + unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2; + uint8_t helper_buf[n_blocks * access_size]; + + /* Read from memory */ + if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK) + return ERROR_FAIL; + + /* Modify and write back */ + memcpy(helper_buf + offset_head, buffer, size); + return target_write_memory(target, address - offset_head, access_size, n_blocks, helper_buf); +} + +/** + * Read one memory item of given "size". Use memory access of given "access_size". + * Read larger section of memory and pick out the required portion, if needed. + * */ +static int read_by_given_size(struct target *target, target_addr_t address, + uint32_t size, uint8_t *buffer, uint32_t access_size) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8); + + if (access_size <= size && address % access_size == 0) + /* Can do the memory access directly without a helper buffer. */ + return target_read_memory(target, address, access_size, size / access_size, buffer); + + unsigned int offset_head = address % access_size; + unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2; + uint8_t helper_buf[n_blocks * access_size]; + + /* Read from memory */ + if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK) + return ERROR_FAIL; + + /* Pick the requested portion from the buffer */ + memcpy(buffer, helper_buf + offset_head, size); + return ERROR_OK; +} + +/** + * Write one memory item using any memory access size that will work. + * Utilize read-modify-write, if needed. + * */ +int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + + /* Find access size that correspond to data size and the alignment. */ + unsigned int preferred_size = size; + while (address % preferred_size != 0) + preferred_size /= 2; + + /* First try the preferred (most natural) access size. */ + if (write_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK) + return ERROR_OK; + + /* On failure, try other access sizes. + Minimize the number of accesses by trying first the largest size. */ + for (unsigned int access_size = 8; access_size > 0; access_size /= 2) { + if (access_size == preferred_size) + /* Already tried this size. */ + continue; + + if (write_by_given_size(target, address, size, buffer, access_size) == ERROR_OK) + return ERROR_OK; + } + + /* No access attempt succeeded. */ + return ERROR_FAIL; +} + +/** + * Read one memory item using any memory access size that will work. + * Read larger section of memory and pick out the required portion, if needed. + * */ +int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + + /* Find access size that correspond to data size and the alignment. */ + unsigned int preferred_size = size; + while (address % preferred_size != 0) + preferred_size /= 2; + + /* First try the preferred (most natural) access size. */ + if (read_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK) + return ERROR_OK; + + /* On failure, try other access sizes. + Minimize the number of accesses by trying first the largest size. */ + for (unsigned int access_size = 8; access_size > 0; access_size /= 2) { + if (access_size == preferred_size) + /* Already tried this size. */ + continue; + + if (read_by_given_size(target, address, size, buffer, access_size) == ERROR_OK) + return ERROR_OK; + } + + /* No access attempt succeeded. */ + return ERROR_FAIL; +} + int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) { LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address); @@ -705,8 +872,9 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) return ERROR_FAIL; } - if (target_read_memory(target, breakpoint->address, 2, breakpoint->length / 2, - breakpoint->orig_instr) != ERROR_OK) { + /* Read the original instruction. */ + if (riscv_read_by_any_size( + target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR, breakpoint->address); return ERROR_FAIL; @@ -714,9 +882,8 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) uint8_t buff[4] = { 0 }; buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c()); - int const retval = target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2, buff); - - if (retval != ERROR_OK) { + /* Write the ebreak instruction. */ + if (riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) { LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); return ERROR_FAIL; @@ -744,40 +911,26 @@ static int remove_trigger(struct target *target, struct trigger *trigger) if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; - int first_hart = -1; - for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - if (first_hart < 0) { - first_hart = hartid; - break; - } - } - assert(first_hart >= 0); - unsigned int i; - for (i = 0; i < r->trigger_count[first_hart]; i++) { + for (i = 0; i < r->trigger_count; i++) { if (r->trigger_unique_id[i] == trigger->unique_id) break; } - if (i >= r->trigger_count[first_hart]) { + if (i >= r->trigger_count) { LOG_ERROR("Couldn't find the hardware resources used by hardware " "trigger."); return ERROR_FAIL; } LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i, trigger->unique_id); - for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - riscv_reg_t tselect; - int result = riscv_get_register_on_hart(target, &tselect, hartid, GDB_REGNO_TSELECT); - if (result != ERROR_OK) - return result; - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i); - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect); - } + + riscv_reg_t tselect; + int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT); + if (result != ERROR_OK) + return result; + riscv_set_register(target, GDB_REGNO_TSELECT, i); + riscv_set_register(target, GDB_REGNO_TDATA1, 0); + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); r->trigger_unique_id[i] = -1; return ERROR_OK; @@ -787,8 +940,9 @@ int riscv_remove_breakpoint(struct target *target, struct breakpoint *breakpoint) { if (breakpoint->type == BKPT_SOFT) { - if (target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2, - breakpoint->orig_instr) != ERROR_OK) { + /* Write the original instruction. */ + if (riscv_write_by_any_size( + target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at " "0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); return ERROR_FAIL; @@ -864,8 +1018,6 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi { struct watchpoint *wp = target->watchpoints; - if (riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, target->rtos->current_thread - 1); LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target)); /*TODO instead of disassembling the instruction that we think caused the @@ -968,7 +1120,7 @@ static int riscv_examine(struct target *target) /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ - riscv_info_t *info = (riscv_info_t *) target->arch_info; + RISCV_INFO(info); uint32_t dtmcontrol = dtmcontrol_scan(target, 0); LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION); @@ -1000,44 +1152,42 @@ static int old_or_new_riscv_poll(struct target *target) return riscv_openocd_poll(target); } +int riscv_select_current_hart(struct target *target) +{ + return riscv_set_current_hartid(target, target->coreid); +} + int halt_prep(struct target *target) { RISCV_INFO(r); - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; - LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target), - target->debug_reason); - if (riscv_set_current_hartid(target, i) != ERROR_OK) + LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target), + target->debug_reason); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + LOG_DEBUG("[%s] Hart is already halted (reason=%d).", + target_name(target), target->debug_reason); + } else { + if (r->halt_prep(target) != ERROR_OK) return ERROR_FAIL; - if (riscv_is_halted(target)) { - LOG_DEBUG("Hart %d is already halted (reason=%d).", i, - target->debug_reason); - } else { - if (r->halt_prep(target) != ERROR_OK) - return ERROR_FAIL; - r->prepped = true; - } + r->prepped = true; } + return ERROR_OK; } int riscv_halt_go_all_harts(struct target *target) { RISCV_INFO(r); - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; - if (riscv_set_current_hartid(target, i) != ERROR_OK) + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + LOG_DEBUG("[%s] Hart is already halted.", target_name(target)); + } else { + if (r->halt_go(target) != ERROR_OK) return ERROR_FAIL; - if (riscv_is_halted(target)) { - LOG_DEBUG("Hart %d is already halted.", i); - } else { - if (r->halt_go(target) != ERROR_OK) - return ERROR_FAIL; - } } riscv_invalidate_register_cache(target); @@ -1110,15 +1260,6 @@ int riscv_halt(struct target *target) return ERROR_FAIL; } - if (riscv_rtos_enabled(target)) { - if (r->rtos_hartid != -1) { - LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid); - target->rtos->current_threadid = r->rtos_hartid + 1; - target->rtos->current_thread = r->rtos_hartid + 1; - } else - LOG_DEBUG("halt requested, but no known RTOS hartid"); - } - return result; } @@ -1140,22 +1281,19 @@ static int riscv_deassert_reset(struct target *target) int riscv_resume_prep_all_harts(struct target *target) { RISCV_INFO(r); - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; - LOG_DEBUG("prep hart %d", i); - if (riscv_set_current_hartid(target, i) != ERROR_OK) + LOG_DEBUG("[%s] prep hart", target_name(target)); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + if (r->resume_prep(target) != ERROR_OK) return ERROR_FAIL; - if (riscv_is_halted(target)) { - if (r->resume_prep(target) != ERROR_OK) - return ERROR_FAIL; - } else { - LOG_DEBUG(" hart %d requested resume, but was already resumed", i); - } + } else { + LOG_DEBUG("[%s] hart requested resume, but was already resumed", + target_name(target)); } - LOG_DEBUG("[%d] mark as prepped", target->coreid); + LOG_DEBUG("[%s] mark as prepped", target_name(target)); r->prepped = true; return ERROR_OK; @@ -1171,13 +1309,12 @@ static int disable_triggers(struct target *target, riscv_reg_t *state) if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; - int hartid = riscv_current_hartid(target); if (r->manual_hwbp_set) { /* Look at every trigger that may have been set. */ riscv_reg_t tselect; if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) return ERROR_FAIL; - for (unsigned t = 0; t < r->trigger_count[hartid]; t++) { + for (unsigned int t = 0; t < r->trigger_count; t++) { if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) return ERROR_FAIL; riscv_reg_t tdata1; @@ -1215,14 +1352,12 @@ static int enable_triggers(struct target *target, riscv_reg_t *state) { RISCV_INFO(r); - int hartid = riscv_current_hartid(target); - if (r->manual_hwbp_set) { /* Look at every trigger that may have been set. */ riscv_reg_t tselect; if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) return ERROR_FAIL; - for (unsigned t = 0; t < r->trigger_count[hartid]; t++) { + for (unsigned int t = 0; t < r->trigger_count; t++) { if (state[t] != 0) { if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) return ERROR_FAIL; @@ -1376,17 +1511,6 @@ static int riscv_target_resume(struct target *target, int current, target_addr_t debug_execution, false); } -static int riscv_select_current_hart(struct target *target) -{ - RISCV_INFO(r); - if (riscv_rtos_enabled(target)) { - if (r->rtos_hartid == -1) - r->rtos_hartid = target->rtos->current_threadid - 1; - return riscv_set_current_hartid(target, r->rtos_hartid); - } else - return riscv_set_current_hartid(target, target->coreid); -} - static int riscv_mmu(struct target *target, int *enabled) { if (!riscv_enable_virt2phys) { @@ -1394,9 +1518,6 @@ static int riscv_mmu(struct target *target, int *enabled) return ERROR_OK; } - if (riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, target->rtos->current_thread - 1); - /* Don't use MMU in explicit or effective M (machine) mode */ riscv_reg_t priv; if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) { @@ -1443,13 +1564,10 @@ static int riscv_address_translate(struct target *target, int mode; uint64_t ppn_value; target_addr_t table_address; - virt2phys_info_t *info; + const virt2phys_info_t *info; uint64_t pte = 0; int i; - if (riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, target->rtos->current_thread - 1); - int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP); if (result != ERROR_OK) return result; @@ -1631,8 +1749,8 @@ static int riscv_get_gdb_reg_list_internal(struct target *target, enum target_register_class reg_class, bool read) { RISCV_INFO(r); - LOG_DEBUG("rtos_hartid=%d, current_hartid=%d, reg_class=%d, read=%d", - r->rtos_hartid, r->current_hartid, reg_class, read); + LOG_DEBUG("current_hartid=%d, reg_class=%d, read=%d", + r->current_hartid, reg_class, read); if (!target->reg_cache) { LOG_ERROR("Target not initialized. Return ERROR_FAIL."); @@ -1702,8 +1820,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, struct reg_param *reg_params, target_addr_t entry_point, target_addr_t exit_point, int timeout_ms, void *arch_info) { - riscv_info_t *info = (riscv_info_t *) target->arch_info; - int hartid = riscv_current_hartid(target); + RISCV_INFO(info); if (num_mem_params > 0) { LOG_ERROR("Memory parameters are not supported for RISC-V algorithms."); @@ -1768,7 +1885,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, reg_mstatus->type->get(reg_mstatus); current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size); uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE; - buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus, + buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus, ie_mask, 0)); reg_mstatus->type->set(reg_mstatus, mstatus_bytes); @@ -1814,7 +1931,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } /* The current hart id might have been changed in poll(). */ - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + if (riscv_select_current_hart(target) != ERROR_OK) return ERROR_FAIL; if (reg_pc->type->get(reg_pc) != ERROR_OK) @@ -1828,12 +1945,12 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, /* Restore Interrupts */ LOG_DEBUG("Restoring Interrupts"); - buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus); + buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus); reg_mstatus->type->set(reg_mstatus, mstatus_bytes); /* Restore registers */ uint8_t buf[8] = { 0 }; - buf_set_u64(buf, 0, info->xlen[0], saved_pc); + buf_set_u64(buf, 0, info->xlen, saved_pc); if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) return ERROR_FAIL; @@ -1849,7 +1966,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } LOG_DEBUG("restore %s", reg_params[i].reg_name); struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false); - buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]); + buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]); if (r->type->set(r, buf) != ERROR_OK) { LOG_ERROR("set(%s) failed", r->name); return ERROR_FAIL; @@ -2004,58 +2121,66 @@ int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason) return ERROR_OK; } +int sample_memory(struct target *target) +{ + RISCV_INFO(r); + + if (!r->sample_buf.buf || !r->sample_config.enabled) + return ERROR_OK; + + LOG_DEBUG("buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size); + + uint64_t start = timeval_ms(); + riscv_sample_buf_maybe_add_timestamp(target, true); + int result = ERROR_OK; + if (r->sample_memory) { + result = r->sample_memory(target, &r->sample_buf, &r->sample_config, + start + TARGET_DEFAULT_POLLING_INTERVAL); + if (result != ERROR_NOT_IMPLEMENTED) + goto exit; + } + + /* Default slow path. */ + while (timeval_ms() - start < TARGET_DEFAULT_POLLING_INTERVAL) { + for (unsigned int i = 0; i < ARRAY_SIZE(r->sample_config.bucket); i++) { + if (r->sample_config.bucket[i].enabled && + r->sample_buf.used + 1 + r->sample_config.bucket[i].size_bytes < r->sample_buf.size) { + assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE); + r->sample_buf.buf[r->sample_buf.used] = i; + result = riscv_read_phys_memory( + target, r->sample_config.bucket[i].address, + r->sample_config.bucket[i].size_bytes, 1, + r->sample_buf.buf + r->sample_buf.used + 1); + if (result == ERROR_OK) + r->sample_buf.used += 1 + r->sample_config.bucket[i].size_bytes; + else + goto exit; + } + } + } + +exit: + riscv_sample_buf_maybe_add_timestamp(target, false); + if (result != ERROR_OK) { + LOG_INFO("Turning off memory sampling because it failed."); + r->sample_config.enabled = false; + } + return result; +} + /*** OpenOCD Interface ***/ int riscv_openocd_poll(struct target *target) { LOG_DEBUG("polling all harts"); int halted_hart = -1; - if (riscv_rtos_enabled(target)) { - /* Check every hart for an event. */ - for (int i = 0; i < riscv_count_harts(target); ++i) { - enum riscv_poll_hart out = riscv_poll_hart(target, i); - switch (out) { - case RPH_NO_CHANGE: - case RPH_DISCOVERED_RUNNING: - continue; - case RPH_DISCOVERED_HALTED: - halted_hart = i; - break; - case RPH_ERROR: - return ERROR_FAIL; - } - } - if (halted_hart == -1) { - LOG_DEBUG(" no harts just halted, target->state=%d", target->state); - return ERROR_OK; - } - LOG_DEBUG(" hart %d halted", halted_hart); - target->state = TARGET_HALTED; - enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart); - if (set_debug_reason(target, halt_reason) != ERROR_OK) - return ERROR_FAIL; - - target->rtos->current_threadid = halted_hart + 1; - target->rtos->current_thread = halted_hart + 1; - riscv_set_rtos_hartid(target, halted_hart); - - /* 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) { + if (target->smp) { unsigned halts_discovered = 0; - unsigned total_targets = 0; unsigned should_remain_halted = 0; unsigned should_resume = 0; unsigned i = 0; for (struct target_list *list = target->head; list; list = list->next, i++) { - total_targets++; struct target *t = list->target; riscv_info_t *r = riscv_info(t); enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid); @@ -2113,15 +2238,29 @@ int riscv_openocd_poll(struct target *target) LOG_DEBUG("resume all"); riscv_resume(target, true, 0, 0, 0, false); } + + /* Sample memory if any target is running. */ + for (struct target_list *list = target->head; list; + list = list->next, i++) { + struct target *t = list->target; + if (t->state == TARGET_RUNNING) { + sample_memory(target); + break; + } + } + return ERROR_OK; } else { enum riscv_poll_hart out = riscv_poll_hart(target, riscv_current_hartid(target)); - if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) + if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) { + if (target->state == TARGET_RUNNING) + sample_memory(target); return ERROR_OK; - else if (out == RPH_ERROR) + } else if (out == RPH_ERROR) { return ERROR_FAIL; + } halted_hart = riscv_current_hartid(target); LOG_DEBUG(" hart %d halted", halted_hart); @@ -2218,32 +2357,86 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec) return ERROR_OK; } -COMMAND_HANDLER(riscv_test_compliance) { - - struct target *target = get_current_target(CMD_CTX); - - RISCV_INFO(r); - - if (CMD_ARGC > 0) { - LOG_ERROR("Command does not take any parameters."); - return ERROR_COMMAND_SYNTAX_ERROR; - } - - if (r->test_compliance) { - return r->test_compliance(target); - } else { - LOG_ERROR("This target does not support this command (may implement an older version of the spec)."); - return ERROR_FAIL; - } -} - COMMAND_HANDLER(riscv_set_prefer_sba) { + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + bool prefer_sba; + LOG_WARNING("`riscv set_prefer_sba` is deprecated. Please use `riscv set_mem_access` instead."); if (CMD_ARGC != 1) { LOG_ERROR("Command takes exactly 1 parameter"); return ERROR_COMMAND_SYNTAX_ERROR; } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_prefer_sba); + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], prefer_sba); + if (prefer_sba) { + /* Use system bus with highest priority */ + r->mem_access_methods[0] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; + } else { + /* Use progbuf with highest priority */ + r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; + } + + /* Reset warning flags */ + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + + return ERROR_OK; +} + +COMMAND_HANDLER(riscv_set_mem_access) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + int progbuf_cnt = 0; + int sysbus_cnt = 0; + int abstract_cnt = 0; + + if (CMD_ARGC < 1 || CMD_ARGC > RISCV_NUM_MEM_ACCESS_METHODS) { + LOG_ERROR("Command takes 1 to %d parameters", RISCV_NUM_MEM_ACCESS_METHODS); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* Check argument validity */ + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if (strcmp("progbuf", CMD_ARGV[i]) == 0) { + progbuf_cnt++; + } else if (strcmp("sysbus", CMD_ARGV[i]) == 0) { + sysbus_cnt++; + } else if (strcmp("abstract", CMD_ARGV[i]) == 0) { + abstract_cnt++; + } else { + LOG_ERROR("Unknown argument '%s'. " + "Must be one of: 'progbuf', 'sysbus' or 'abstract'.", CMD_ARGV[i]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + if (progbuf_cnt > 1 || sysbus_cnt > 1 || abstract_cnt > 1) { + LOG_ERROR("Syntax error - duplicate arguments to `riscv set_mem_access`."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* Args are valid, store them */ + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_UNSPECIFIED; + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if (strcmp("progbuf", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_PROGBUF; + else if (strcmp("sysbus", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_SYSBUS; + else if (strcmp("abstract", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_ABSTRACT; + } + + /* Reset warning flags */ + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + return ERROR_OK; } @@ -2257,109 +2450,180 @@ COMMAND_HANDLER(riscv_set_enable_virtual) return ERROR_OK; } -void parse_error(const char *string, char c, unsigned position) +int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val) { - char buf[position+2]; - for (unsigned i = 0; i < position; i++) - buf[i] = ' '; - buf[position] = '^'; - buf[position + 1] = 0; + char *args = strdup(tcl_arg); + if (!args) + return ERROR_FAIL; - LOG_ERROR("Parse error at character %c in:", c); - LOG_ERROR("%s", string); - LOG_ERROR("%s", buf); -} - -int parse_ranges(range_t **ranges, const char **argv) -{ - for (unsigned pass = 0; pass < 2; pass++) { - unsigned range = 0; + /* For backward compatibility, allow multiple parameters within one TCL argument, separated by ',' */ + char *arg = strtok(args, ","); + while (arg) { unsigned low = 0; - bool parse_low = true; unsigned high = 0; - for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) { - char c = argv[0][i]; - if (isspace(c)) { - /* Ignore whitespace. */ - continue; + char *name = NULL; + + char *dash = strchr(arg, '-'); + char *equals = strchr(arg, '='); + unsigned int pos; + + if (!dash && !equals) { + /* Expecting single register number. */ + if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) { + LOG_ERROR("Failed to parse single register number from '%s'.", arg); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; } - - if (parse_low) { - if (isdigit(c)) { - low *= 10; - low += c - '0'; - } else if (c == '-') { - parse_low = false; - } else if (c == ',' || c == 0) { - if (pass == 1) { - (*ranges)[range].low = low; - (*ranges)[range].high = low; - } - low = 0; - range++; - } else { - parse_error(argv[0], c, i); - return ERROR_COMMAND_SYNTAX_ERROR; - } - - } else { - if (isdigit(c)) { - high *= 10; - high += c - '0'; - } else if (c == ',' || c == 0) { - parse_low = true; - if (pass == 1) { - (*ranges)[range].low = low; - (*ranges)[range].high = high; - } - low = 0; - high = 0; - range++; - } else { - parse_error(argv[0], c, i); - return ERROR_COMMAND_SYNTAX_ERROR; - } + } else if (dash && !equals) { + /* Expecting register range - two numbers separated by a dash: ##-## */ + *dash = 0; + dash++; + if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) { + LOG_ERROR("Failed to parse single register number from '%s'.", arg); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; } - } - - if (pass == 0) { - free(*ranges); - *ranges = calloc(range + 2, sizeof(range_t)); - if (!*ranges) + if (sscanf(dash, "%u%n", &high, &pos) != 1 || pos != strlen(dash)) { + LOG_ERROR("Failed to parse single register number from '%s'.", dash); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; + } + if (high < low) { + LOG_ERROR("Incorrect range encountered [%u, %u].", low, high); + free(args); return ERROR_FAIL; + } + } else if (!dash && equals) { + /* Expecting single register number with textual name specified: ##=name */ + *equals = 0; + equals++; + if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) { + LOG_ERROR("Failed to parse single register number from '%s'.", arg); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + name = calloc(1, strlen(equals) + strlen(reg_type) + 2); + if (!name) { + LOG_ERROR("Failed to allocate register name."); + free(args); + return ERROR_FAIL; + } + + /* Register prefix: "csr_" or "custom_" */ + strcpy(name, reg_type); + name[strlen(reg_type)] = '_'; + + if (sscanf(equals, "%[_a-zA-Z0-9]%n", name + strlen(reg_type) + 1, &pos) != 1 || pos != strlen(equals)) { + LOG_ERROR("Failed to parse register name from '%s'.", equals); + free(args); + free(name); + return ERROR_COMMAND_SYNTAX_ERROR; + } } else { - (*ranges)[range].low = 1; - (*ranges)[range].high = 0; + LOG_ERROR("Invalid argument '%s'.", arg); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; } + + high = high > low ? high : low; + + if (high > max_val) { + LOG_ERROR("Cannot expose %s register number %u, maximum allowed value is %u.", reg_type, high, max_val); + free(name); + free(args); + return ERROR_FAIL; + } + + /* Check for overlap, name uniqueness. */ + range_list_t *entry; + list_for_each_entry(entry, ranges, list) { + if ((entry->low <= high) && (low <= entry->high)) { + if (low == high) + LOG_WARNING("Duplicate %s register number - " + "Register %u has already been exposed previously", reg_type, low); + else + LOG_WARNING("Overlapping register ranges - Register range starting from %u overlaps " + "with already exposed register/range at %u.", low, entry->low); + } + + if (entry->name && name && (strcasecmp(entry->name, name) == 0)) { + LOG_ERROR("Duplicate register name \"%s\" found.", name); + free(name); + free(args); + return ERROR_FAIL; + } + } + + range_list_t *range = calloc(1, sizeof(range_list_t)); + if (!range) { + LOG_ERROR("Failed to allocate range list."); + free(name); + free(args); + return ERROR_FAIL; + } + + range->low = low; + range->high = high; + range->name = name; + list_add(&range->list, ranges); + + arg = strtok(NULL, ","); } + free(args); return ERROR_OK; } COMMAND_HANDLER(riscv_set_expose_csrs) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + if (CMD_ARGC == 0) { + LOG_ERROR("Command expects parameters"); return ERROR_COMMAND_SYNTAX_ERROR; } - return parse_ranges(&expose_csr, CMD_ARGV); + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(info); + int ret = ERROR_OK; + + for (unsigned int i = 0; i < CMD_ARGC; i++) { + ret = parse_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff); + if (ret != ERROR_OK) + break; + } + + return ret; } COMMAND_HANDLER(riscv_set_expose_custom) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + if (CMD_ARGC == 0) { + LOG_ERROR("Command expects parameters"); return ERROR_COMMAND_SYNTAX_ERROR; } - return parse_ranges(&expose_custom, CMD_ARGV); + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(info); + int ret = ERROR_OK; + + for (unsigned int i = 0; i < CMD_ARGC; i++) { + ret = parse_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff); + if (ret != ERROR_OK) + break; + } + + return ret; } COMMAND_HANDLER(riscv_authdata_read) { - if (CMD_ARGC != 0) { - LOG_ERROR("Command takes no parameters"); + unsigned int index = 0; + if (CMD_ARGC == 0) { + /* nop */ + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index); + } else { + LOG_ERROR("Command takes at most one parameter"); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -2377,7 +2641,7 @@ COMMAND_HANDLER(riscv_authdata_read) if (r->authdata_read) { uint32_t value; - if (r->authdata_read(target, &value) != ERROR_OK) + if (r->authdata_read(target, &value, index) != ERROR_OK) return ERROR_FAIL; command_print_sameline(CMD, "0x%08" PRIx32, value); return ERROR_OK; @@ -2389,19 +2653,26 @@ COMMAND_HANDLER(riscv_authdata_read) COMMAND_HANDLER(riscv_authdata_write) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 argument"); + uint32_t value; + unsigned int index = 0; + + if (CMD_ARGC == 0) { + /* nop */ + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value); + } else if (CMD_ARGC == 2) { + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + } else { + LOG_ERROR("Command takes at most 2 arguments"); return ERROR_COMMAND_SYNTAX_ERROR; } struct target *target = get_current_target(CMD_CTX); RISCV_INFO(r); - uint32_t value; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value); - if (r->authdata_write) { - return r->authdata_write(target, value); + return r->authdata_write(target, value, index); } else { LOG_ERROR("authdata_write is not implemented for this target."); return ERROR_FAIL; @@ -2617,13 +2888,40 @@ COMMAND_HANDLER(riscv_set_ebreaku) return ERROR_OK; } +COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key, + unsigned int value) +{ + char full_key[80]; + snprintf(full_key, sizeof(full_key), "%s.%s", section, key); + command_print(CMD, "%-21s %3d", full_key, value); + return 0; +} + +COMMAND_HANDLER(handle_info) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + /* This output format can be fed directly into TCL's "array set". */ + + riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target)); + riscv_enumerate_triggers(target); + riscv_print_info_line(CMD, "hart", "trigger_count", + r->trigger_count); + + if (r->print_info) + return CALL_COMMAND_HANDLER(r->print_info, target); + + return 0; +} + static const struct command_registration riscv_exec_command_handlers[] = { { - .name = "test_compliance", - .handler = riscv_test_compliance, - .usage = "", + .name = "info", + .handler = handle_info, .mode = COMMAND_EXEC, - .help = "Runs a basic compliance test suite against the RISC-V Debug Spec." + .usage = "", + .help = "Displays some information OpenOCD detected about the target." }, { .name = "set_command_timeout_sec", @@ -2647,6 +2945,14 @@ static const struct command_registration riscv_exec_command_handlers[] = { .help = "When on, prefer to use System Bus Access to access memory. " "When off (default), prefer to use the Program Buffer to access memory." }, + { + .name = "set_mem_access", + .handler = riscv_set_mem_access, + .mode = COMMAND_ANY, + .usage = "method1 [method2] [method3]", + .help = "Set which memory access methods shall be used and in which order " + "of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'." + }, { .name = "set_enable_virtual", .handler = riscv_set_enable_virtual, @@ -2659,8 +2965,8 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "expose_csrs", .handler = riscv_set_expose_csrs, - .mode = COMMAND_ANY, - .usage = "n0[-m0][,n1[-m1]]...", + .mode = COMMAND_CONFIG, + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", .help = "Configure a list of inclusive ranges for CSRs to expose in " "addition to the standard ones. This must be executed before " "`init`." @@ -2668,8 +2974,8 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "expose_custom", .handler = riscv_set_expose_custom, - .mode = COMMAND_ANY, - .usage = "n0[-m0][,n1[-m1]]...", + .mode = COMMAND_CONFIG, + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", .help = "Configure a list of inclusive ranges for custom registers to " "expose. custom0 is accessed as abstract register number 0xc000, " "etc. This must be executed before `init`." @@ -2677,16 +2983,18 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "authdata_read", .handler = riscv_authdata_read, - .usage = "", + .usage = "[index]", .mode = COMMAND_ANY, - .help = "Return the 32-bit value read from authdata." + .help = "Return the 32-bit value read from authdata or authdata0 " + "(index=0), or authdata1 (index=1)." }, { .name = "authdata_write", .handler = riscv_authdata_write, .mode = COMMAND_ANY, - .usage = "value", - .help = "Write the 32-bit value to authdata." + .usage = "[index] value", + .help = "Write the 32-bit value to authdata or authdata0 (index=0), " + "or authdata1 (index=1)." }, { .name = "dmi_read", @@ -2836,6 +3144,7 @@ static unsigned int riscv_data_bits(struct target *target) struct target_type riscv_target = { .name = "riscv", + .target_create = riscv_create_target, .init_target = riscv_init_target, .deinit_target = riscv_deinit_target, .examine = riscv_examine, @@ -2889,49 +3198,37 @@ void riscv_info_init(struct target *target, riscv_info_t *r) r->dtm_version = 1; r->registers_initialized = false; r->current_hartid = target->coreid; + r->version_specific = NULL; memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id)); - for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) - r->xlen[h] = -1; + r->xlen = -1; + + r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; + + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + + INIT_LIST_HEAD(&r->expose_csr); + INIT_LIST_HEAD(&r->expose_custom); } static int riscv_resume_go_all_harts(struct target *target) { RISCV_INFO(r); - /* Dummy variables to make mingw32-gcc happy. */ - int first = 0; - int last = 1; - int step = 1; - switch (resume_order) { - case RO_NORMAL: - first = 0; - last = riscv_count_harts(target) - 1; - step = 1; - break; - case RO_REVERSED: - first = riscv_count_harts(target) - 1; - last = 0; - step = -1; - break; - default: - assert(0); - } - - for (int i = first; i != last + step; i += step) { - if (!riscv_hart_enabled(target, i)) - continue; - - LOG_DEBUG("resuming hart %d", i); - if (riscv_set_current_hartid(target, i) != ERROR_OK) + LOG_DEBUG("[%s] resuming hart", target_name(target)); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + if (r->resume_go(target) != ERROR_OK) return ERROR_FAIL; - if (riscv_is_halted(target)) { - if (r->resume_go(target) != ERROR_OK) - return ERROR_FAIL; - } else { - LOG_DEBUG(" hart %d requested resume, but was already resumed", i); - } + } else { + LOG_DEBUG("[%s] hart requested resume, but was already resumed", + target_name(target)); } riscv_invalidate_register_cache(target); @@ -2941,17 +3238,9 @@ static int riscv_resume_go_all_harts(struct target *target) int riscv_step_rtos_hart(struct target *target) { RISCV_INFO(r); - int hartid = r->current_hartid; - if (riscv_rtos_enabled(target)) { - hartid = r->rtos_hartid; - if (hartid == -1) { - LOG_DEBUG("GDB has asked me to step \"any\" thread, so I'm stepping hart 0."); - hartid = 0; - } - } - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + if (riscv_select_current_hart(target) != ERROR_OK) return ERROR_FAIL; - LOG_DEBUG("stepping hart %d", hartid); + LOG_DEBUG("[%s] stepping", target_name(target)); if (!riscv_is_halted(target)) { LOG_ERROR("Hart isn't halted before single step!"); @@ -2970,7 +3259,7 @@ int riscv_step_rtos_hart(struct target *target) return ERROR_OK; } -bool riscv_supports_extension(struct target *target, int hartid, char letter) +bool riscv_supports_extension(struct target *target, char letter) { RISCV_INFO(r); unsigned num; @@ -2980,24 +3269,13 @@ bool riscv_supports_extension(struct target *target, int hartid, char letter) num = letter - 'A'; else return false; - return r->misa[hartid] & (1 << num); + return r->misa & BIT(num); } unsigned riscv_xlen(const struct target *target) -{ - return riscv_xlen_of_hart(target, riscv_current_hartid(target)); -} - -int riscv_xlen_of_hart(const struct target *target, int hartid) { RISCV_INFO(r); - assert(r->xlen[hartid] != -1); - return r->xlen[hartid]; -} - -bool riscv_rtos_enabled(const struct target *target) -{ - return false; + return r->xlen; } int riscv_set_current_hartid(struct target *target, int hartid) @@ -3008,16 +3286,10 @@ int riscv_set_current_hartid(struct target *target, int hartid) int previous_hartid = riscv_current_hartid(target); r->current_hartid = hartid; - assert(riscv_hart_enabled(target, hartid)); LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid); if (r->select_current_hart(target) != ERROR_OK) return ERROR_FAIL; - /* This might get called during init, in which case we shouldn't be - * setting up the register cache. */ - if (target_was_examined(target) && riscv_rtos_enabled(target)) - riscv_invalidate_register_cache(target); - return ERROR_OK; } @@ -3041,19 +3313,6 @@ int riscv_current_hartid(const struct target *target) return r->current_hartid; } -void riscv_set_all_rtos_harts(struct target *target) -{ - RISCV_INFO(r); - r->rtos_hartid = -1; -} - -void riscv_set_rtos_hartid(struct target *target, int hartid) -{ - LOG_DEBUG("setting RTOS hartid %d", hartid); - RISCV_INFO(r); - r->rtos_hartid = hartid; -} - int riscv_count_harts(struct target *target) { if (!target) @@ -3064,11 +3323,6 @@ int riscv_count_harts(struct target *target) return r->hart_count(target); } -bool riscv_has_register(struct target *target, int hartid, int regid) -{ - return 1; -} - /** * If write is true: * return true iff we are guaranteed that the register will contain exactly @@ -3085,7 +3339,7 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write) (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)) return true; - /* Most CSRs won't change value on us, but we can't assume it about rbitrary + /* Most CSRs won't change value on us, but we can't assume it about arbitrary * CSRs. */ switch (regno) { case GDB_REGNO_DPC: @@ -3122,75 +3376,67 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write) * This function is called when the debug user wants to change the value of a * register. The new value may be cached, and may not be written until the hart * is resumed. */ -int riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v) -{ - return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v); -} - -int riscv_set_register_on_hart(struct target *target, int hartid, - enum gdb_regno regid, uint64_t value) +int riscv_set_register(struct target *target, enum gdb_regno regid, riscv_reg_t value) { RISCV_INFO(r); - LOG_DEBUG("{%d} %s <- %" PRIx64, hartid, gdb_regno_name(regid), value); + LOG_DEBUG("[%s] %s <- %" PRIx64, target_name(target), gdb_regno_name(regid), value); assert(r->set_register); + keep_alive(); + /* TODO: Hack to deal with gdb that thinks these registers still exist. */ if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 && - riscv_supports_extension(target, hartid, 'E')) + riscv_supports_extension(target, 'E')) return ERROR_OK; struct reg *reg = &target->reg_cache->reg_list[regid]; buf_set_u64(reg->value, 0, reg->size, value); - int result = r->set_register(target, hartid, regid, value); + int result = r->set_register(target, regid, value); if (result == ERROR_OK) reg->valid = gdb_regno_cacheable(regid, true); else reg->valid = false; - LOG_DEBUG("[%s]{%d} wrote 0x%" PRIx64 " to %s valid=%d", - target_name(target), hartid, value, reg->name, reg->valid); + LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d", + target_name(target), value, reg->name, reg->valid); return result; } int riscv_get_register(struct target *target, riscv_reg_t *value, - enum gdb_regno r) -{ - return riscv_get_register_on_hart(target, value, - riscv_current_hartid(target), r); -} - -int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value, - int hartid, enum gdb_regno regid) + enum gdb_regno regid) { RISCV_INFO(r); + keep_alive(); + struct reg *reg = &target->reg_cache->reg_list[regid]; if (!reg->exist) { - LOG_DEBUG("[%s]{%d} %s does not exist.", - target_name(target), hartid, gdb_regno_name(regid)); + LOG_DEBUG("[%s] %s does not exist.", + target_name(target), gdb_regno_name(regid)); return ERROR_FAIL; } - if (reg && reg->valid && hartid == riscv_current_hartid(target)) { + if (reg && reg->valid) { *value = buf_get_u64(reg->value, 0, reg->size); - LOG_DEBUG("{%d} %s: %" PRIx64 " (cached)", hartid, + LOG_DEBUG("[%s] %s: %" PRIx64 " (cached)", target_name(target), gdb_regno_name(regid), *value); return ERROR_OK; } /* TODO: Hack to deal with gdb that thinks these registers still exist. */ if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && - riscv_supports_extension(target, hartid, 'E')) { + riscv_supports_extension(target, 'E')) { *value = 0; return ERROR_OK; } - int result = r->get_register(target, value, hartid, regid); + int result = r->get_register(target, value, regid); if (result == ERROR_OK) reg->valid = gdb_regno_cacheable(regid, false); - LOG_DEBUG("{%d} %s: %" PRIx64, hartid, gdb_regno_name(regid), *value); + LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target), + gdb_regno_name(regid), *value); return result; } @@ -3216,7 +3462,7 @@ enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid) size_t riscv_debug_buffer_size(struct target *target) { RISCV_INFO(r); - return r->debug_buffer_size[riscv_current_hartid(target)]; + return r->debug_buffer_size; } int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn) @@ -3262,15 +3508,6 @@ int riscv_dmi_write_u64_bits(struct target *target) return r->dmi_write_u64_bits(target); } -bool riscv_hart_enabled(struct target *target, int hartid) -{ - /* FIXME: Add a hart mask to the RTOS. */ - if (riscv_rtos_enabled(target)) - return hartid < riscv_count_harts(target); - - return hartid == target->coreid; -} - /** * Count triggers, and initialize trigger_count for each hart. * trigger_count is initialized even if this function fails to discover @@ -3287,59 +3524,58 @@ int riscv_enumerate_triggers(struct target *target) r->triggers_enumerated = true; /* At the very least we tried. */ - for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; + riscv_reg_t tselect; + int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT); + /* If tselect is not readable, the trigger module is likely not + * implemented. There are no triggers to enumerate then and no error + * should be thrown. */ + if (result != ERROR_OK) { + LOG_DEBUG("[%s] Cannot access tselect register. " + "Assuming that triggers are not implemented.", target_name(target)); + r->trigger_count = 0; + return ERROR_OK; + } - riscv_reg_t tselect; - int result = riscv_get_register_on_hart(target, &tselect, hartid, - GDB_REGNO_TSELECT); + for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) { + r->trigger_count = t; + + /* If we can't write tselect, then this hart does not support triggers. */ + if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) + break; + uint64_t tselect_rb; + result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT); + if (result != ERROR_OK) + return result; + /* Mask off the top bit, which is used as tdrmode in old + * implementations. */ + tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1)); + if (tselect_rb != t) + break; + uint64_t tdata1; + result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1); if (result != ERROR_OK) return result; - for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) { - r->trigger_count[hartid] = t; - - /* If we can't write tselect, then this hart does not support triggers. */ - if (riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t) != ERROR_OK) + int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); + if (type == 0) + break; + switch (type) { + case 1: + /* On these older cores we don't support software using + * triggers. */ + riscv_set_register(target, GDB_REGNO_TDATA1, 0); break; - uint64_t tselect_rb; - result = riscv_get_register_on_hart(target, &tselect_rb, hartid, - GDB_REGNO_TSELECT); - if (result != ERROR_OK) - return result; - /* Mask off the top bit, which is used as tdrmode in old - * implementations. */ - tselect_rb &= ~(1ULL << (riscv_xlen(target)-1)); - if (tselect_rb != t) + case 2: + if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) + riscv_set_register(target, GDB_REGNO_TDATA1, 0); break; - uint64_t tdata1; - result = riscv_get_register_on_hart(target, &tdata1, hartid, - GDB_REGNO_TDATA1); - if (result != ERROR_OK) - return result; - - int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); - if (type == 0) - break; - switch (type) { - case 1: - /* On these older cores we don't support software using - * triggers. */ - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); - break; - case 2: - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); - break; - } } - - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect); - - LOG_INFO("[%d] Found %d triggers", hartid, r->trigger_count[hartid]); } + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); + + LOG_INFO("[%s] Found %d triggers", target_name(target), r->trigger_count); + return ERROR_OK; } @@ -3549,8 +3785,8 @@ static int register_get(struct reg *reg) } reg->valid = gdb_regno_cacheable(reg->number, false); char *str = buf_to_hex_str(reg->value, reg->size); - LOG_DEBUG("[%d]{%d} read 0x%s from %s (valid=%d)", target->coreid, - riscv_current_hartid(target), str, reg->name, reg->valid); + LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target), + str, reg->name, reg->valid); free(str); return ERROR_OK; } @@ -3562,10 +3798,15 @@ static int register_set(struct reg *reg, uint8_t *buf) RISCV_INFO(r); char *str = buf_to_hex_str(buf, reg->size); - LOG_DEBUG("[%d]{%d} write 0x%s to %s (valid=%d)", target->coreid, - riscv_current_hartid(target), str, reg->name, reg->valid); + LOG_DEBUG("[%s] write 0x%s to %s (valid=%d)", target_name(target), + str, reg->name, reg->valid); free(str); + /* Exit early for writing x0, which on the hardware would be ignored, and we + * don't want to update our cache. */ + if (reg->number == GDB_REGNO_ZERO) + return ERROR_OK; + memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8)); reg->valid = gdb_regno_cacheable(reg->number, true); @@ -3625,13 +3866,10 @@ int riscv_init_registers(struct target *target) target->reg_cache->name = "RISC-V Registers"; target->reg_cache->num_regs = GDB_REGNO_COUNT; - if (expose_custom) { - for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) { - for (unsigned number = expose_custom[i].low; - number <= expose_custom[i].high; - number++) - target->reg_cache->num_regs++; - } + if (!list_empty(&info->expose_custom)) { + range_list_t *entry; + list_for_each_entry(entry, &info->expose_custom, list) + target->reg_cache->num_regs += entry->high - entry->low + 1; } LOG_DEBUG("create register cache for %d registers", @@ -3650,8 +3888,6 @@ int riscv_init_registers(struct target *target) return ERROR_FAIL; char *reg_name = info->reg_names; - int hartid = riscv_current_hartid(target); - static struct reg_feature feature_cpu = { .name = "org.gnu.gdb.riscv.cpu" }; @@ -3709,35 +3945,35 @@ int riscv_init_registers(struct target *target) */ info->vector_uint8.type = &type_uint8; - info->vector_uint8.count = info->vlenb[hartid]; + info->vector_uint8.count = info->vlenb; info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint8_vector.id = "bytes"; info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint8_vector.reg_type_vector = &info->vector_uint8; info->vector_uint16.type = &type_uint16; - info->vector_uint16.count = info->vlenb[hartid] / 2; + info->vector_uint16.count = info->vlenb / 2; info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint16_vector.id = "shorts"; info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint16_vector.reg_type_vector = &info->vector_uint16; info->vector_uint32.type = &type_uint32; - info->vector_uint32.count = info->vlenb[hartid] / 4; + info->vector_uint32.count = info->vlenb / 4; info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint32_vector.id = "words"; info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint32_vector.reg_type_vector = &info->vector_uint32; info->vector_uint64.type = &type_uint64; - info->vector_uint64.count = info->vlenb[hartid] / 8; + info->vector_uint64.count = info->vlenb / 8; info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint64_vector.id = "longs"; info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint64_vector.reg_type_vector = &info->vector_uint64; info->vector_uint128.type = &type_uint128; - info->vector_uint128.count = info->vlenb[hartid] / 16; + info->vector_uint128.count = info->vlenb / 16; info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint128_vector.id = "quads"; info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR; @@ -3745,28 +3981,28 @@ int riscv_init_registers(struct target *target) info->vector_fields[0].name = "b"; info->vector_fields[0].type = &info->type_uint8_vector; - if (info->vlenb[hartid] >= 2) { + if (info->vlenb >= 2) { info->vector_fields[0].next = info->vector_fields + 1; info->vector_fields[1].name = "s"; info->vector_fields[1].type = &info->type_uint16_vector; } else { info->vector_fields[0].next = NULL; } - if (info->vlenb[hartid] >= 4) { + if (info->vlenb >= 4) { info->vector_fields[1].next = info->vector_fields + 2; info->vector_fields[2].name = "w"; info->vector_fields[2].type = &info->type_uint32_vector; } else { info->vector_fields[1].next = NULL; } - if (info->vlenb[hartid] >= 8) { + if (info->vlenb >= 8) { info->vector_fields[2].next = info->vector_fields + 3; info->vector_fields[3].name = "l"; info->vector_fields[3].type = &info->type_uint64_vector; } else { info->vector_fields[2].next = NULL; } - if (info->vlenb[hartid] >= 16) { + if (info->vlenb >= 16) { info->vector_fields[3].next = info->vector_fields + 4; info->vector_fields[4].name = "q"; info->vector_fields[4].type = &info->type_uint128_vector; @@ -3791,7 +4027,6 @@ int riscv_init_registers(struct target *target) qsort(csr_info, ARRAY_SIZE(csr_info), sizeof(*csr_info), cmp_csr_info); unsigned csr_info_index = 0; - unsigned custom_range_index = 0; int custom_within_range = 0; riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t)); @@ -3818,7 +4053,7 @@ int riscv_init_registers(struct target *target) * of other things to break in that case as well. */ if (number <= GDB_REGNO_XPR31) { r->exist = number <= GDB_REGNO_XPR15 || - !riscv_supports_extension(target, hartid, 'E'); + !riscv_supports_extension(target, 'E'); /* TODO: For now we fake that all GPRs exist because otherwise gdb * doesn't work. */ r->exist = true; @@ -3930,13 +4165,13 @@ int riscv_init_registers(struct target *target) r->feature = &feature_cpu; } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { r->caller_save = true; - if (riscv_supports_extension(target, hartid, 'D')) { + if (riscv_supports_extension(target, 'D')) { r->size = 64; - if (riscv_supports_extension(target, hartid, 'F')) + if (riscv_supports_extension(target, 'F')) r->reg_data_type = &type_ieee_single_double; else r->reg_data_type = &type_ieee_double; - } else if (riscv_supports_extension(target, hartid, 'F')) { + } else if (riscv_supports_extension(target, 'F')) { r->reg_data_type = &type_ieee_single; r->size = 32; } else { @@ -4067,7 +4302,7 @@ int riscv_init_registers(struct target *target) case CSR_FFLAGS: case CSR_FRM: case CSR_FCSR: - r->exist = riscv_supports_extension(target, hartid, 'F'); + r->exist = riscv_supports_extension(target, 'F'); r->group = "float"; r->feature = &feature_fpu; break; @@ -4081,15 +4316,15 @@ int riscv_init_registers(struct target *target) case CSR_SCAUSE: case CSR_STVAL: case CSR_SATP: - r->exist = riscv_supports_extension(target, hartid, 'S'); + r->exist = riscv_supports_extension(target, 'S'); break; case CSR_MEDELEG: case CSR_MIDELEG: /* "In systems with only M-mode, or with both M-mode and * U-mode but without U-mode trap support, the medeleg and * mideleg registers should not exist." */ - r->exist = riscv_supports_extension(target, hartid, 'S') || - riscv_supports_extension(target, hartid, 'N'); + r->exist = riscv_supports_extension(target, 'S') || + riscv_supports_extension(target, 'N'); break; case CSR_PMPCFG1: @@ -4166,18 +4401,25 @@ int riscv_init_registers(struct target *target) case CSR_VL: case CSR_VTYPE: case CSR_VLENB: - r->exist = riscv_supports_extension(target, hartid, 'V'); + r->exist = riscv_supports_extension(target, 'V'); break; } - if (!r->exist && expose_csr) { - for (unsigned i = 0; expose_csr[i].low <= expose_csr[i].high; i++) { - if (csr_number >= expose_csr[i].low && csr_number <= expose_csr[i].high) { - LOG_INFO("Exposing additional CSR %d", csr_number); + if (!r->exist && !list_empty(&info->expose_csr)) { + range_list_t *entry; + list_for_each_entry(entry, &info->expose_csr, list) + if ((entry->low <= csr_number) && (csr_number <= entry->high)) { + if (entry->name) { + *reg_name = 0; + r->name = entry->name; + } + + LOG_DEBUG("Exposing additional CSR %d (name=%s)", + csr_number, entry->name ? entry->name : reg_name); + r->exist = true; break; } - } } } else if (number == GDB_REGNO_PRIV) { @@ -4188,8 +4430,8 @@ int riscv_init_registers(struct target *target) } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) { r->caller_save = false; - r->exist = riscv_supports_extension(target, hartid, 'V') && info->vlenb[hartid]; - r->size = info->vlenb[hartid] * 8; + r->exist = riscv_supports_extension(target, 'V') && info->vlenb; + r->size = info->vlenb * 8; sprintf(reg_name, "v%d", number - GDB_REGNO_V0); r->group = "vector"; r->feature = &feature_vector; @@ -4197,10 +4439,10 @@ int riscv_init_registers(struct target *target) } else if (number >= GDB_REGNO_COUNT) { /* Custom registers. */ - assert(expose_custom); + assert(!list_empty(&info->expose_custom)); + + range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list); - range_t *range = &expose_custom[custom_range_index]; - assert(range->low <= range->high); unsigned custom_number = range->low + custom_within_range; r->group = "custom"; @@ -4212,18 +4454,27 @@ int riscv_init_registers(struct target *target) ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number; sprintf(reg_name, "custom%d", custom_number); + if (range->name) { + *reg_name = 0; + r->name = range->name; + } + + LOG_DEBUG("Exposing additional custom register %d (name=%s)", + number, range->name ? range->name : reg_name); + custom_within_range++; if (custom_within_range > range->high - range->low) { custom_within_range = 0; - custom_range_index++; + list_rotate_left(&info->expose_custom); } } - if (reg_name[0]) + if (reg_name[0]) { r->name = reg_name; - reg_name += strlen(reg_name) + 1; - assert(reg_name < info->reg_names + target->reg_cache->num_regs * - max_reg_name_len); + reg_name += strlen(reg_name) + 1; + assert(reg_name < info->reg_names + target->reg_cache->num_regs * + max_reg_name_len); + } r->value = info->reg_cache_values[number]; } diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index d943134e2..d0f4f6ec0 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -10,6 +10,7 @@ struct riscv_program; #include "gdb_regs.h" #include "jtag/jtag.h" #include "target/register.h" +#include /* The register cache is statically allocated. */ #define RISCV_MAX_HARTS 1024 @@ -26,6 +27,8 @@ struct riscv_program; # define PG_MAX_LEVEL 4 +#define RISCV_NUM_MEM_ACCESS_METHODS 3 + extern struct target_type riscv011_target; extern struct target_type riscv013_target; @@ -36,6 +39,13 @@ typedef uint64_t riscv_reg_t; typedef uint32_t riscv_insn_t; typedef uint64_t riscv_addr_t; +enum riscv_mem_access_method { + RISCV_MEM_ACCESS_UNSPECIFIED, + RISCV_MEM_ACCESS_PROGBUF, + RISCV_MEM_ACCESS_SYSBUS, + RISCV_MEM_ACCESS_ABSTRACT +}; + enum riscv_halt_reason { RISCV_HALT_INTERRUPT, RISCV_HALT_BREAKPOINT, @@ -51,15 +61,35 @@ typedef struct { unsigned custom_number; } riscv_reg_info_t; +#define RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE 0x80 +#define RISCV_SAMPLE_BUF_TIMESTAMP_AFTER 0x81 +struct riscv_sample_buf { + uint8_t *buf; + unsigned int used; + unsigned int size; +}; + +typedef struct { + bool enabled; + struct { + bool enabled; + target_addr_t address; + uint32_t size_bytes; + } bucket[16]; +} riscv_sample_config_t; + +typedef struct { + struct list_head list; + uint16_t low, high; + char *name; +} range_list_t; + typedef struct { unsigned dtm_version; struct command_context *cmd_ctx; void *version_specific; - /* The hart that the RTOS thinks is currently being debugged. */ - int rtos_hartid; - /* The hart that is currently being debugged. Note that this is * different than the hartid that the RTOS is expected to use. This * one will change all the time, it's more of a global argument to @@ -76,13 +106,13 @@ typedef struct { char *reg_names; /* It's possible that each core has a different supported ISA set. */ - int xlen[RISCV_MAX_HARTS]; - riscv_reg_t misa[RISCV_MAX_HARTS]; + int xlen; + riscv_reg_t misa; /* Cached value of vlenb. 0 if vlenb is not readable for some reason. */ - unsigned vlenb[RISCV_MAX_HARTS]; + unsigned int vlenb; /* The number of triggers per hart. */ - unsigned trigger_count[RISCV_MAX_HARTS]; + unsigned int trigger_count; /* For each physical trigger, contains -1 if the hwbp is available, or the * unique_id of the breakpoint/watchpoint that is using it. @@ -91,7 +121,7 @@ typedef struct { int trigger_unique_id[RISCV_MAX_HWBPS]; /* The number of entries in the debug buffer. */ - int debug_buffer_size[RISCV_MAX_HARTS]; + int debug_buffer_size; /* This avoids invalidating the register cache too often. */ bool registers_initialized; @@ -112,10 +142,8 @@ typedef struct { /* 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)(struct target *target, riscv_reg_t *value, int regid); + int (*set_register)(struct target *target, int regid, uint64_t value); int (*get_register_buf)(struct target *target, uint8_t *buf, int regno); int (*set_register_buf)(struct target *target, int regno, const uint8_t *buf); @@ -143,8 +171,8 @@ typedef struct { void (*fill_dmi_read_u64)(struct target *target, char *buf, int a); void (*fill_dmi_nop_u64)(struct target *target, char *buf); - int (*authdata_read)(struct target *target, uint32_t *value); - int (*authdata_write)(struct target *target, uint32_t value); + int (*authdata_read)(struct target *target, uint32_t *value, unsigned int index); + int (*authdata_write)(struct target *target, uint32_t value, unsigned int index); int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address); int (*dmi_write)(struct target *target, uint32_t address, uint32_t value); @@ -152,7 +180,10 @@ typedef struct { int (*test_sba_config_reg)(struct target *target, target_addr_t legal_address, uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test); - int (*test_compliance)(struct target *target); + int (*sample_memory)(struct target *target, + struct riscv_sample_buf *buf, + riscv_sample_config_t *config, + int64_t until_ms); int (*read_memory)(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment); @@ -161,6 +192,8 @@ typedef struct { int (*hart_count)(struct target *target); unsigned (*data_bits)(struct target *target); + COMMAND_HELPER((*print_info), struct target *target); + /* Storage for vector register types. */ struct reg_data_type_vector vector_uint8; struct reg_data_type_vector vector_uint16; @@ -179,8 +212,31 @@ typedef struct { /* 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; + + /* Memory access methods to use, ordered by priority, highest to lowest. */ + int mem_access_methods[RISCV_NUM_MEM_ACCESS_METHODS]; + + /* Different memory regions may need different methods but single configuration is applied + * for all. Following flags are used to warn only once about failing memory access method. */ + bool mem_access_progbuf_warn; + bool mem_access_sysbus_warn; + bool mem_access_abstract_warn; + + /* In addition to the ones in the standard spec, we'll also expose additional + * CSRs in this list. */ + struct list_head expose_csr; + /* Same, but for custom registers. + * Custom registers are for non-standard extensions and use abstract register numbers + * from range 0xc000 ... 0xffff. */ + struct list_head expose_custom; + + riscv_sample_config_t sample_config; + struct riscv_sample_buf sample_buf; } riscv_info_t; +COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key, + unsigned int value); + typedef struct { uint8_t tunneled_dr_width; struct scan_field tunneled_dr[4]; @@ -205,8 +261,6 @@ extern int riscv_command_timeout_sec; /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/ extern int riscv_reset_timeout_sec; -extern bool riscv_prefer_sba; - extern bool riscv_enable_virtual; extern bool riscv_ebreakm; extern bool riscv_ebreaks; @@ -216,7 +270,10 @@ extern bool riscv_ebreaku; * that provides that. */ static inline riscv_info_t *riscv_info(const struct target *target) __attribute__((unused)); static inline riscv_info_t *riscv_info(const struct target *target) -{ return target->arch_info; } +{ + assert(target->arch_info); + return target->arch_info; +} #define RISCV_INFO(R) riscv_info_t *R = riscv_info(target); extern uint8_t ir_dtmcontrol[4]; @@ -269,44 +326,30 @@ void riscv_info_init(struct target *target, riscv_info_t *r); * then the only hart. */ int riscv_step_rtos_hart(struct target *target); -bool riscv_supports_extension(struct target *target, int hartid, char letter); +bool riscv_supports_extension(struct target *target, char letter); /* Returns XLEN for the given (or current) hart. */ 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); +int riscv_xlen_of_hart(const struct target *target); /* Sets the current hart, which is the hart that will actually be used when * issuing debug commands. */ int riscv_set_current_hartid(struct target *target, int hartid); +int riscv_select_current_hart(struct target *target); int riscv_current_hartid(const struct target *target); /*** Support functions for the RISC-V 'RTOS', which provides multihart support * without requiring multiple targets. */ -/* When using the RTOS to debug, this selects the hart that is currently being - * debugged. This doesn't propagate to the hardware. */ -void riscv_set_all_rtos_harts(struct target *target); -void riscv_set_rtos_hartid(struct target *target, int hartid); - /* Lists the number of harts in the system, which are assumed to be * consecutive and start with mhartid=0. */ int riscv_count_harts(struct target *target); -/* Returns TRUE if the target has the given register on the given hart. */ -bool riscv_has_register(struct target *target, int hartid, int regid); - /** 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); /* Checks the state of the current hart -- "is_halted" checks the actual * on-device register. */ @@ -329,9 +372,6 @@ int riscv_dmi_write_u64_bits(struct target *target); /* Invalidates the register cache. */ void riscv_invalidate_register_cache(struct target *target); -/* Returns TRUE when a hart is enabled in this target. */ -bool riscv_hart_enabled(struct target *target, int hartid); - int riscv_enumerate_triggers(struct target *target); int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint); @@ -356,4 +396,7 @@ 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); +int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer); +int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer); + #endif diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c index 0072b9afe..b347212d3 100644 --- a/src/target/riscv/riscv_semihosting.c +++ b/src/target/riscv/riscv_semihosting.c @@ -85,12 +85,15 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval) if (result != ERROR_OK) return SEMI_ERROR; - uint8_t tmp[12]; + uint8_t tmp_buf[12]; - /* Read the current instruction, including the bracketing */ - *retval = target_read_memory(target, pc - 4, 2, 6, tmp); - if (*retval != ERROR_OK) - return SEMI_ERROR; + /* Read three uncompressed instructions: The previous, the current one (pointed to by PC) and the next one */ + for (int i = 0; i < 3; i++) { + /* Instruction memories may not support arbitrary read size. Use any size that will work. */ + *retval = riscv_read_by_any_size(target, (pc - 4) + 4 * i, 4, tmp_buf + 4 * i); + if (*retval != ERROR_OK) + return SEMI_ERROR; + } /* * The instructions that trigger a semihosting call, @@ -100,9 +103,9 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval) * 00100073 ebreak * 40705013 srai zero,zero,0x7 */ - 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); + uint32_t pre = target_buffer_get_u32(target, tmp_buf); + uint32_t ebreak = target_buffer_get_u32(target, tmp_buf + 4); + uint32_t post = target_buffer_get_u32(target, tmp_buf + 8); LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc); if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) { From 37bce983956cf34f725b096a33a1a6c16ac58d71 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Wed, 6 Oct 2021 23:27:16 +0200 Subject: [PATCH 11/15] openocd: remove 'src' prefix from #include path There is no reason to add the 'src' prefix. Remove it. Change-Id: Id7d7ee8b3807fb90381cc1d6d545321020bc06c1 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6634 Tested-by: jenkins Reviewed-by: Tomas Vanek --- src/jtag/drivers/jlink.c | 2 +- src/target/arm_coresight.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c index 319ca380a..53ae1dfae 100644 --- a/src/jtag/drivers/jlink.c +++ b/src/jtag/drivers/jlink.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/target/arm_coresight.h b/src/target/arm_coresight.h index 42e6c5eb6..a08f4fb53 100644 --- a/src/target/arm_coresight.h +++ b/src/target/arm_coresight.h @@ -11,7 +11,7 @@ #include #include -#include +#include #define ARM_CS_ALIGN (0x1000) From 0fb131c23a1cba9ea128db9c72fcd6479e508513 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Fri, 8 Oct 2021 18:43:06 +0200 Subject: [PATCH 12/15] riscv: use relative path to include contrib's data Doxygen cannot resolve the path of the files in folder contrib. Use a path relative to current folder, as done in other files. Change-Id: If39b416ed422b4854dd108777fa32dd4c809450a Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6635 Tested-by: jenkins Reviewed-by: Tim Newsome --- src/target/riscv/riscv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 7579e0f0c..084939e11 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1987,10 +1987,10 @@ static int riscv_checksum_memory(struct target *target, LOG_DEBUG("address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count); static const uint8_t riscv32_crc_code[] = { -#include "contrib/loaders/checksum/riscv32_crc.inc" +#include "../../../contrib/loaders/checksum/riscv32_crc.inc" }; static const uint8_t riscv64_crc_code[] = { -#include "contrib/loaders/checksum/riscv64_crc.inc" +#include "../../../contrib/loaders/checksum/riscv64_crc.inc" }; static const uint8_t *crc_code; From 69190094ad209b2d9091d7c2ca90ac7d49382d61 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Mon, 16 Sep 2019 10:50:04 +0200 Subject: [PATCH 13/15] hla: improve readability of struct hl_interface_s initialization The initialization is barely readable, while actually only few fields are set with value nor zero nor NULL. Rewrite the initialization using C99 struct designations. Change-Id: I4d288e6536ebe7110a184db6540223fc67361ec3 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6636 Tested-by: jenkins Reviewed-by: Tomas Vanek --- src/jtag/hla/hla_interface.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/jtag/hla/hla_interface.c b/src/jtag/hla/hla_interface.c index b16a930a9..fc362c040 100644 --- a/src/jtag/hla/hla_interface.c +++ b/src/jtag/hla/hla_interface.c @@ -35,7 +35,21 @@ #include -static struct hl_interface_s hl_if = { {0, 0, { 0 }, { 0 }, HL_TRANSPORT_UNKNOWN, false, -1, false, 7184}, 0, 0 }; +static struct hl_interface_s hl_if = { + .param = { + .device_desc = NULL, + .serial = NULL, + .vid = { 0 }, + .pid = { 0 }, + .transport = HL_TRANSPORT_UNKNOWN, + .connect_under_reset = false, + .initial_interface_speed = -1, + .use_stlink_tcp = false, + .stlink_tcp_port = 7184, + }, + .layout = NULL, + .handle = NULL, +}; int hl_interface_open(enum hl_transports tr) { From bba2eecbc2b7d738e6f948b85ec420c084b28022 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Fri, 8 Oct 2021 18:15:12 +0200 Subject: [PATCH 14/15] jtag/aice: remove unused exported struct aice_interface_param_s The struct aice_interface_param_s is declared but is never referenced. Drop it. Change-Id: I4e6493d4baf292bb55dbd40228d4fa7c9e2afab5 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6637 Tested-by: jenkins Reviewed-by: Tomas Vanek --- src/jtag/aice/aice_interface.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/jtag/aice/aice_interface.h b/src/jtag/aice/aice_interface.h index 220b0b04d..3bddfa342 100644 --- a/src/jtag/aice/aice_interface.h +++ b/src/jtag/aice/aice_interface.h @@ -19,17 +19,6 @@ #ifndef OPENOCD_JTAG_AICE_AICE_INTERFACE_H #define OPENOCD_JTAG_AICE_AICE_INTERFACE_H -struct aice_interface_param_s { - /** */ - const char *device_desc; - /** */ - const char *serial; - /** */ - uint16_t vid; - /** */ - uint16_t pid; -}; - int aice_init_targets(void); int aice_scan_jtag_chain(void); From 97db87c22eec204edfebd55973ee1211a1dba6d3 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Thu, 7 Oct 2021 12:04:46 +0200 Subject: [PATCH 15/15] jtag/core: remove unused variable Commit e3f3f60a02ab ("adapter speed: require init script setting and centralize activation from drivers to core.c") has already dropped the only use of variable 'jtag_speed'. Remove the variable. Change-Id: Iff096df0022982cf90795aa62d6b3406203f7b14 Signed-off-by: Antonio Borneo Reviewed-on: https://review.openocd.org/c/openocd/+/6638 Tested-by: jenkins Reviewed-by: Tomas Vanek --- src/jtag/core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/jtag/core.c b/src/jtag/core.c index 51875a00a..2de5fda47 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -128,7 +128,6 @@ static int speed_khz; /* speed to fallback to when RCLK is requested but not supported */ static int rclk_fallback_speed_khz; static enum {CLOCK_MODE_UNSELECTED, CLOCK_MODE_KHZ, CLOCK_MODE_RCLK} clock_mode; -static int jtag_speed; /* FIXME: change name to this variable, it is not anymore JTAG only */ static struct adapter_driver *jtag; @@ -1804,7 +1803,6 @@ static int jtag_rclk_to_speed(unsigned fallback_speed_khz, int *speed) static int jtag_set_speed(int speed) { - jtag_speed = speed; /* this command can be called during CONFIG, * in which case jtag isn't initialized */ return jtag ? jtag->speed(speed) : ERROR_OK;