From: Tim Newsome Date: Wed, 1 Sep 2021 22:00:46 +0000 (-0700) Subject: Upstream a whole host of RISC-V changes. X-Git-Tag: v0.12.0-rc1~473 X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=commitdiff_plain;h=615709d14049027e6172fbb7f6cf6c898eefaea9;hp=f4612e06c61f6c46cff936d2c6b48d6f2627ff61 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 --- diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c index 3c062e1501..a3d68a91f3 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 cb518a8911..b5104d5306 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 - * \RcsrTdataTwo. + * 2: Matches when any compare value is greater than (unsigned) or + * equal to \RcsrTdataTwo. * - * 3: Matches when the value is less than (unsigned) \RcsrTdataTwo. + * 3: Matches when any compare value is less than (unsigned) + * \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. + * 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 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_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_MSELECT_OFFSET 25 -#define CSR_TEXTRA32_MSELECT_LENGTH 1 -#define CSR_TEXTRA32_MSELECT (0x1U << CSR_TEXTRA32_MSELECT_OFFSET) +#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 998290cb96..8faa154bae 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 2fa925aff7..62a04f0933 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 86a95f6350..ae0b6e481b 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 fdebdd413d..006266a90c 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,54 +1715,49 @@ 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; - } + 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; } + } - /* Without knowing anything else we can at least mess with the - * program buffer. */ - r->debug_buffer_size[i] = info->progbufsize; + /* 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[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); - return ERROR_FAIL; - } + int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64); + if (result == ERROR_OK) + r->xlen = 64; + else + r->xlen = 32; - if (riscv_supports_extension(target, i, 'V')) { - if (discover_vlenb(target, i) != ERROR_OK) - return ERROR_FAIL; - } + 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; + } - /* Now init registers based on what we discovered. */ - if (riscv_init_registers(target) != ERROR_OK) + 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, i, r->xlen[i], - r->misa[i]); + /* 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); - } + if (!halted) + riscv013_step_or_resume_current_hart(target, false, false); target_set_examined(target); @@ -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; - - return riscv_xlen(target); + 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 (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); + + 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); - return read_memory_abstract(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) { + if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) { + LOG_ERROR("BUG: Unsupported size for memory write: %d", size); + return ERROR_FAIL; + } + + int ret = ERROR_FAIL; + RISCV_INFO(r); RISCV013_INFO(info); - if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba) - return write_memory_progbuf(target, address, size, count, buffer); + char *progbuf_result = "disabled"; + char *sysbus_result = "disabled"; + char *abstract_result = "disabled"; - 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); - } + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { + int method = r->mem_access_methods[i]; - if (has_sufficient_progbuf(target, 3)) - return write_memory_progbuf(target, address, size, count, buffer); + 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); - return write_memory_abstract(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 07fb95550c..7579e0f0c3 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,77 +618,104 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid, LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%" PRIx64 " to tdata1 it contains 0x%" PRIx64, tdata1, tdata1_rb); - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); + riscv_set_register(target, GDB_REGNO_TDATA1, 0); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address); + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); return ERROR_OK; } -static int add_trigger(struct target *target, struct trigger *trigger) +static int maybe_add_trigger_t6(struct target *target, + struct trigger *trigger, uint64_t tdata1) { RISCV_INFO(r); - if (riscv_enumerate_triggers(target) != ERROR_OK) - return ERROR_FAIL; + /* tselect is already set */ + if (tdata1 & (CSR_MCONTROL6_EXECUTE | CSR_MCONTROL6_STORE | CSR_MCONTROL6_LOAD)) { + /* Trigger is already in use, presumably by user code. */ + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* address/data match trigger */ + tdata1 |= MCONTROL_DMODE(riscv_xlen(target)); + tdata1 = set_field(tdata1, CSR_MCONTROL6_ACTION, + MCONTROL_ACTION_DEBUG_MODE); + tdata1 = set_field(tdata1, CSR_MCONTROL6_MATCH, MCONTROL_MATCH_EQUAL); + tdata1 |= CSR_MCONTROL6_M; + if (r->misa & (1 << ('H' - 'A'))) + tdata1 |= CSR_MCONTROL6_VS | CSR_MCONTROL6_VU; + if (r->misa & (1 << ('S' - 'A'))) + tdata1 |= CSR_MCONTROL6_S; + if (r->misa & (1 << ('U' - 'A'))) + tdata1 |= CSR_MCONTROL6_U; - /* In RTOS mode, we need to set the same trigger in the same slot on every - * hart, to keep up the illusion that each hart is a thread running on the - * same core. */ + if (trigger->execute) + tdata1 |= CSR_MCONTROL6_EXECUTE; + if (trigger->read) + tdata1 |= CSR_MCONTROL6_LOAD; + if (trigger->write) + tdata1 |= CSR_MCONTROL6_STORE; - /* Otherwise, we just set the trigger on the one hart this target deals - * with. */ + riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); - riscv_reg_t tselect[RISCV_MAX_HARTS]; + uint64_t tdata1_rb; + int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1); + if (result != ERROR_OK) + return result; + LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb); - int first_hart = -1; - for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - if (first_hart < 0) - first_hart = hartid; - int result = riscv_get_register_on_hart(target, &tselect[hartid], - hartid, GDB_REGNO_TSELECT); - if (result != ERROR_OK) - return result; + if (tdata1 != tdata1_rb) { + LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%" + PRIx64 " to tdata1 it contains 0x%" PRIx64, + tdata1, tdata1_rb); + riscv_set_register(target, GDB_REGNO_TDATA1, 0); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - assert(first_hart >= 0); + + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); + + return ERROR_OK; +} + +static int add_trigger(struct target *target, struct trigger *trigger) +{ + RISCV_INFO(r); + + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + + riscv_reg_t tselect; + if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; unsigned int i; - for (i = 0; i < r->trigger_count[first_hart]; i++) { + for (i = 0; i < r->trigger_count; i++) { if (r->trigger_unique_id[i] != -1) continue; - riscv_set_register_on_hart(target, first_hart, GDB_REGNO_TSELECT, i); + riscv_set_register(target, GDB_REGNO_TSELECT, i); uint64_t tdata1; - int result = riscv_get_register_on_hart(target, &tdata1, first_hart, - GDB_REGNO_TDATA1); + int result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1); if (result != ERROR_OK) return result; int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); result = ERROR_OK; - for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - if (hartid > first_hart) - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i); - switch (type) { - case 1: - result = maybe_add_trigger_t1(target, hartid, trigger, tdata1); - break; - case 2: - result = maybe_add_trigger_t2(target, hartid, trigger, tdata1); - break; - default: - LOG_DEBUG("trigger %d has unknown type %d", i, type); - continue; - } - - if (result != ERROR_OK) + switch (type) { + case 1: + result = maybe_add_trigger_t1(target, trigger, tdata1); + break; + case 2: + result = maybe_add_trigger_t2(target, trigger, tdata1); + break; + case 6: + result = maybe_add_trigger_t6(target, trigger, tdata1); + break; + default: + LOG_DEBUG("trigger %d has unknown type %d", i, type); continue; } @@ -674,14 +728,9 @@ static int add_trigger(struct target *target, struct trigger *trigger) break; } - for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, - tselect[hartid]); - } + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); - if (i >= r->trigger_count[first_hart]) { + if (i >= r->trigger_count) { LOG_ERROR("Couldn't find an available hardware trigger."); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -689,6 +738,124 @@ static int add_trigger(struct target *target, struct trigger *trigger) return ERROR_OK; } +/** + * Write one memory item of given "size". Use memory access of given "access_size". + * Utilize read-modify-write, if needed. + * */ +static int write_by_given_size(struct target *target, target_addr_t address, + uint32_t size, uint8_t *buffer, uint32_t access_size) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8); + + if (access_size <= size && address % access_size == 0) + /* Can do the memory access directly without a helper buffer. */ + return target_write_memory(target, address, access_size, size / access_size, buffer); + + unsigned int offset_head = address % access_size; + unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2; + uint8_t helper_buf[n_blocks * access_size]; + + /* Read from memory */ + if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK) + return ERROR_FAIL; + + /* Modify and write back */ + memcpy(helper_buf + offset_head, buffer, size); + return target_write_memory(target, address - offset_head, access_size, n_blocks, helper_buf); +} + +/** + * Read one memory item of given "size". Use memory access of given "access_size". + * Read larger section of memory and pick out the required portion, if needed. + * */ +static int read_by_given_size(struct target *target, target_addr_t address, + uint32_t size, uint8_t *buffer, uint32_t access_size) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8); + + if (access_size <= size && address % access_size == 0) + /* Can do the memory access directly without a helper buffer. */ + return target_read_memory(target, address, access_size, size / access_size, buffer); + + unsigned int offset_head = address % access_size; + unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2; + uint8_t helper_buf[n_blocks * access_size]; + + /* Read from memory */ + if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK) + return ERROR_FAIL; + + /* Pick the requested portion from the buffer */ + memcpy(buffer, helper_buf + offset_head, size); + return ERROR_OK; +} + +/** + * Write one memory item using any memory access size that will work. + * Utilize read-modify-write, if needed. + * */ +int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + + /* Find access size that correspond to data size and the alignment. */ + unsigned int preferred_size = size; + while (address % preferred_size != 0) + preferred_size /= 2; + + /* First try the preferred (most natural) access size. */ + if (write_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK) + return ERROR_OK; + + /* On failure, try other access sizes. + Minimize the number of accesses by trying first the largest size. */ + for (unsigned int access_size = 8; access_size > 0; access_size /= 2) { + if (access_size == preferred_size) + /* Already tried this size. */ + continue; + + if (write_by_given_size(target, address, size, buffer, access_size) == ERROR_OK) + return ERROR_OK; + } + + /* No access attempt succeeded. */ + return ERROR_FAIL; +} + +/** + * Read one memory item using any memory access size that will work. + * Read larger section of memory and pick out the required portion, if needed. + * */ +int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + + /* Find access size that correspond to data size and the alignment. */ + unsigned int preferred_size = size; + while (address % preferred_size != 0) + preferred_size /= 2; + + /* First try the preferred (most natural) access size. */ + if (read_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK) + return ERROR_OK; + + /* On failure, try other access sizes. + Minimize the number of accesses by trying first the largest size. */ + for (unsigned int access_size = 8; access_size > 0; access_size /= 2) { + if (access_size == preferred_size) + /* Already tried this size. */ + continue; + + if (read_by_given_size(target, address, size, buffer, access_size) == ERROR_OK) + return ERROR_OK; + } + + /* No access attempt succeeded. */ + return ERROR_FAIL; +} + int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) { LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address); @@ -705,8 +872,9 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) return ERROR_FAIL; } - if (target_read_memory(target, breakpoint->address, 2, breakpoint->length / 2, - breakpoint->orig_instr) != ERROR_OK) { + /* Read the original instruction. */ + if (riscv_read_by_any_size( + target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR, breakpoint->address); return ERROR_FAIL; @@ -714,9 +882,8 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) uint8_t buff[4] = { 0 }; buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c()); - int const retval = target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2, buff); - - if (retval != ERROR_OK) { + /* Write the ebreak instruction. */ + if (riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) { LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); return ERROR_FAIL; @@ -744,40 +911,26 @@ static int remove_trigger(struct target *target, struct trigger *trigger) if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; - int first_hart = -1; - for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - if (first_hart < 0) { - first_hart = hartid; - break; - } - } - assert(first_hart >= 0); - unsigned int i; - for (i = 0; i < r->trigger_count[first_hart]; i++) { + for (i = 0; i < r->trigger_count; i++) { if (r->trigger_unique_id[i] == trigger->unique_id) break; } - if (i >= r->trigger_count[first_hart]) { + if (i >= r->trigger_count) { LOG_ERROR("Couldn't find the hardware resources used by hardware " "trigger."); return ERROR_FAIL; } LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i, trigger->unique_id); - for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; - riscv_reg_t tselect; - int result = riscv_get_register_on_hart(target, &tselect, hartid, GDB_REGNO_TSELECT); - if (result != ERROR_OK) - return result; - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i); - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect); - } + + riscv_reg_t tselect; + int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT); + if (result != ERROR_OK) + return result; + riscv_set_register(target, GDB_REGNO_TSELECT, i); + riscv_set_register(target, GDB_REGNO_TDATA1, 0); + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); r->trigger_unique_id[i] = -1; return ERROR_OK; @@ -787,8 +940,9 @@ int riscv_remove_breakpoint(struct target *target, struct breakpoint *breakpoint) { if (breakpoint->type == BKPT_SOFT) { - if (target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2, - breakpoint->orig_instr) != ERROR_OK) { + /* Write the original instruction. */ + if (riscv_write_by_any_size( + target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at " "0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); return ERROR_FAIL; @@ -864,8 +1018,6 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi { struct watchpoint *wp = target->watchpoints; - if (riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, target->rtos->current_thread - 1); LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target)); /*TODO instead of disassembling the instruction that we think caused the @@ -968,7 +1120,7 @@ static int riscv_examine(struct target *target) /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ - riscv_info_t *info = (riscv_info_t *) target->arch_info; + RISCV_INFO(info); uint32_t dtmcontrol = dtmcontrol_scan(target, 0); LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol); info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION); @@ -1000,44 +1152,42 @@ static int old_or_new_riscv_poll(struct target *target) return riscv_openocd_poll(target); } +int riscv_select_current_hart(struct target *target) +{ + return riscv_set_current_hartid(target, target->coreid); +} + int halt_prep(struct target *target) { RISCV_INFO(r); - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; - LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target), - target->debug_reason); - if (riscv_set_current_hartid(target, i) != ERROR_OK) + LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target), + target->debug_reason); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + LOG_DEBUG("[%s] Hart is already halted (reason=%d).", + target_name(target), target->debug_reason); + } else { + if (r->halt_prep(target) != ERROR_OK) return ERROR_FAIL; - if (riscv_is_halted(target)) { - LOG_DEBUG("Hart %d is already halted (reason=%d).", i, - target->debug_reason); - } else { - if (r->halt_prep(target) != ERROR_OK) - return ERROR_FAIL; - r->prepped = true; - } + r->prepped = true; } + return ERROR_OK; } int riscv_halt_go_all_harts(struct target *target) { RISCV_INFO(r); - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; - if (riscv_set_current_hartid(target, i) != ERROR_OK) + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + LOG_DEBUG("[%s] Hart is already halted.", target_name(target)); + } else { + if (r->halt_go(target) != ERROR_OK) return ERROR_FAIL; - if (riscv_is_halted(target)) { - LOG_DEBUG("Hart %d is already halted.", i); - } else { - if (r->halt_go(target) != ERROR_OK) - return ERROR_FAIL; - } } riscv_invalidate_register_cache(target); @@ -1110,15 +1260,6 @@ int riscv_halt(struct target *target) return ERROR_FAIL; } - if (riscv_rtos_enabled(target)) { - if (r->rtos_hartid != -1) { - LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid); - target->rtos->current_threadid = r->rtos_hartid + 1; - target->rtos->current_thread = r->rtos_hartid + 1; - } else - LOG_DEBUG("halt requested, but no known RTOS hartid"); - } - return result; } @@ -1140,22 +1281,19 @@ static int riscv_deassert_reset(struct target *target) int riscv_resume_prep_all_harts(struct target *target) { RISCV_INFO(r); - for (int i = 0; i < riscv_count_harts(target); ++i) { - if (!riscv_hart_enabled(target, i)) - continue; - LOG_DEBUG("prep hart %d", i); - if (riscv_set_current_hartid(target, i) != ERROR_OK) + LOG_DEBUG("[%s] prep hart", target_name(target)); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + if (r->resume_prep(target) != ERROR_OK) return ERROR_FAIL; - if (riscv_is_halted(target)) { - if (r->resume_prep(target) != ERROR_OK) - return ERROR_FAIL; - } else { - LOG_DEBUG(" hart %d requested resume, but was already resumed", i); - } + } else { + LOG_DEBUG("[%s] hart requested resume, but was already resumed", + target_name(target)); } - LOG_DEBUG("[%d] mark as prepped", target->coreid); + LOG_DEBUG("[%s] mark as prepped", target_name(target)); r->prepped = true; return ERROR_OK; @@ -1171,13 +1309,12 @@ static int disable_triggers(struct target *target, riscv_reg_t *state) if (riscv_enumerate_triggers(target) != ERROR_OK) return ERROR_FAIL; - int hartid = riscv_current_hartid(target); if (r->manual_hwbp_set) { /* Look at every trigger that may have been set. */ riscv_reg_t tselect; if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) return ERROR_FAIL; - for (unsigned t = 0; t < r->trigger_count[hartid]; t++) { + for (unsigned int t = 0; t < r->trigger_count; t++) { if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) return ERROR_FAIL; riscv_reg_t tdata1; @@ -1215,14 +1352,12 @@ static int enable_triggers(struct target *target, riscv_reg_t *state) { RISCV_INFO(r); - int hartid = riscv_current_hartid(target); - if (r->manual_hwbp_set) { /* Look at every trigger that may have been set. */ riscv_reg_t tselect; if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) return ERROR_FAIL; - for (unsigned t = 0; t < r->trigger_count[hartid]; t++) { + for (unsigned int t = 0; t < r->trigger_count; t++) { if (state[t] != 0) { if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) return ERROR_FAIL; @@ -1376,17 +1511,6 @@ static int riscv_target_resume(struct target *target, int current, target_addr_t debug_execution, false); } -static int riscv_select_current_hart(struct target *target) -{ - RISCV_INFO(r); - if (riscv_rtos_enabled(target)) { - if (r->rtos_hartid == -1) - r->rtos_hartid = target->rtos->current_threadid - 1; - return riscv_set_current_hartid(target, r->rtos_hartid); - } else - return riscv_set_current_hartid(target, target->coreid); -} - static int riscv_mmu(struct target *target, int *enabled) { if (!riscv_enable_virt2phys) { @@ -1394,9 +1518,6 @@ static int riscv_mmu(struct target *target, int *enabled) return ERROR_OK; } - if (riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, target->rtos->current_thread - 1); - /* Don't use MMU in explicit or effective M (machine) mode */ riscv_reg_t priv; if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) { @@ -1443,13 +1564,10 @@ static int riscv_address_translate(struct target *target, int mode; uint64_t ppn_value; target_addr_t table_address; - virt2phys_info_t *info; + const virt2phys_info_t *info; uint64_t pte = 0; int i; - if (riscv_rtos_enabled(target)) - riscv_set_current_hartid(target, target->rtos->current_thread - 1); - int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP); if (result != ERROR_OK) return result; @@ -1631,8 +1749,8 @@ static int riscv_get_gdb_reg_list_internal(struct target *target, enum target_register_class reg_class, bool read) { RISCV_INFO(r); - LOG_DEBUG("rtos_hartid=%d, current_hartid=%d, reg_class=%d, read=%d", - r->rtos_hartid, r->current_hartid, reg_class, read); + LOG_DEBUG("current_hartid=%d, reg_class=%d, read=%d", + r->current_hartid, reg_class, read); if (!target->reg_cache) { LOG_ERROR("Target not initialized. Return ERROR_FAIL."); @@ -1702,8 +1820,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, struct reg_param *reg_params, target_addr_t entry_point, target_addr_t exit_point, int timeout_ms, void *arch_info) { - riscv_info_t *info = (riscv_info_t *) target->arch_info; - int hartid = riscv_current_hartid(target); + RISCV_INFO(info); if (num_mem_params > 0) { LOG_ERROR("Memory parameters are not supported for RISC-V algorithms."); @@ -1768,7 +1885,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, reg_mstatus->type->get(reg_mstatus); current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size); uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE; - buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus, + buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus, ie_mask, 0)); reg_mstatus->type->set(reg_mstatus, mstatus_bytes); @@ -1814,7 +1931,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } /* The current hart id might have been changed in poll(). */ - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + if (riscv_select_current_hart(target) != ERROR_OK) return ERROR_FAIL; if (reg_pc->type->get(reg_pc) != ERROR_OK) @@ -1828,12 +1945,12 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, /* Restore Interrupts */ LOG_DEBUG("Restoring Interrupts"); - buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus); + buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus); reg_mstatus->type->set(reg_mstatus, mstatus_bytes); /* Restore registers */ uint8_t buf[8] = { 0 }; - buf_set_u64(buf, 0, info->xlen[0], saved_pc); + buf_set_u64(buf, 0, info->xlen, saved_pc); if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) return ERROR_FAIL; @@ -1849,7 +1966,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } LOG_DEBUG("restore %s", reg_params[i].reg_name); struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false); - buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]); + buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]); if (r->type->set(r, buf) != ERROR_OK) { LOG_ERROR("set(%s) failed", r->name); return ERROR_FAIL; @@ -2004,58 +2121,66 @@ int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason) return ERROR_OK; } -/*** OpenOCD Interface ***/ -int riscv_openocd_poll(struct target *target) +int sample_memory(struct target *target) { - LOG_DEBUG("polling all harts"); - int halted_hart = -1; - if (riscv_rtos_enabled(target)) { - /* Check every hart for an event. */ - for (int i = 0; i < riscv_count_harts(target); ++i) { - enum riscv_poll_hart out = riscv_poll_hart(target, i); - switch (out) { - case RPH_NO_CHANGE: - case RPH_DISCOVERED_RUNNING: - continue; - case RPH_DISCOVERED_HALTED: - halted_hart = i; - break; - case RPH_ERROR: - return ERROR_FAIL; + RISCV_INFO(r); + + if (!r->sample_buf.buf || !r->sample_config.enabled) + return ERROR_OK; + + LOG_DEBUG("buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size); + + uint64_t start = timeval_ms(); + riscv_sample_buf_maybe_add_timestamp(target, true); + int result = ERROR_OK; + if (r->sample_memory) { + result = r->sample_memory(target, &r->sample_buf, &r->sample_config, + start + TARGET_DEFAULT_POLLING_INTERVAL); + if (result != ERROR_NOT_IMPLEMENTED) + goto exit; + } + + /* Default slow path. */ + while (timeval_ms() - start < TARGET_DEFAULT_POLLING_INTERVAL) { + for (unsigned int i = 0; i < ARRAY_SIZE(r->sample_config.bucket); i++) { + if (r->sample_config.bucket[i].enabled && + r->sample_buf.used + 1 + r->sample_config.bucket[i].size_bytes < r->sample_buf.size) { + assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE); + r->sample_buf.buf[r->sample_buf.used] = i; + result = riscv_read_phys_memory( + target, r->sample_config.bucket[i].address, + r->sample_config.bucket[i].size_bytes, 1, + r->sample_buf.buf + r->sample_buf.used + 1); + if (result == ERROR_OK) + r->sample_buf.used += 1 + r->sample_config.bucket[i].size_bytes; + else + goto exit; } } - if (halted_hart == -1) { - LOG_DEBUG(" no harts just halted, target->state=%d", target->state); - return ERROR_OK; - } - LOG_DEBUG(" hart %d halted", halted_hart); - - target->state = TARGET_HALTED; - enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart); - if (set_debug_reason(target, halt_reason) != ERROR_OK) - return ERROR_FAIL; + } - target->rtos->current_threadid = halted_hart + 1; - target->rtos->current_thread = halted_hart + 1; - riscv_set_rtos_hartid(target, halted_hart); +exit: + riscv_sample_buf_maybe_add_timestamp(target, false); + if (result != ERROR_OK) { + LOG_INFO("Turning off memory sampling because it failed."); + r->sample_config.enabled = false; + } + return result; +} - /* If we're here then at least one hart triggered. That means we want - * to go and halt _every_ hart (configured with -rtos riscv) in the - * system, as that's the invariant we hold here. Some harts might have - * already halted (as we're either in single-step mode or they also - * triggered a breakpoint), so don't attempt to halt those harts. - * riscv_halt() will do all that for us. */ - riscv_halt(target); +/*** OpenOCD Interface ***/ +int riscv_openocd_poll(struct target *target) +{ + LOG_DEBUG("polling all harts"); + int halted_hart = -1; - } else if (target->smp) { + if (target->smp) { unsigned halts_discovered = 0; - unsigned total_targets = 0; unsigned should_remain_halted = 0; unsigned should_resume = 0; unsigned i = 0; for (struct target_list *list = target->head; list; list = list->next, i++) { - total_targets++; struct target *t = list->target; riscv_info_t *r = riscv_info(t); enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid); @@ -2113,15 +2238,29 @@ int riscv_openocd_poll(struct target *target) LOG_DEBUG("resume all"); riscv_resume(target, true, 0, 0, 0, false); } + + /* Sample memory if any target is running. */ + for (struct target_list *list = target->head; list; + list = list->next, i++) { + struct target *t = list->target; + if (t->state == TARGET_RUNNING) { + sample_memory(target); + break; + } + } + return ERROR_OK; } else { enum riscv_poll_hart out = riscv_poll_hart(target, riscv_current_hartid(target)); - if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) + if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) { + if (target->state == TARGET_RUNNING) + sample_memory(target); return ERROR_OK; - else if (out == RPH_ERROR) + } else if (out == RPH_ERROR) { return ERROR_FAIL; + } halted_hart = riscv_current_hartid(target); LOG_DEBUG(" hart %d halted", halted_hart); @@ -2218,32 +2357,86 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec) return ERROR_OK; } -COMMAND_HANDLER(riscv_test_compliance) { - +COMMAND_HANDLER(riscv_set_prefer_sba) +{ struct target *target = get_current_target(CMD_CTX); - RISCV_INFO(r); - - if (CMD_ARGC > 0) { - LOG_ERROR("Command does not take any parameters."); + bool prefer_sba; + LOG_WARNING("`riscv set_prefer_sba` is deprecated. Please use `riscv set_mem_access` instead."); + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); return ERROR_COMMAND_SYNTAX_ERROR; } - - if (r->test_compliance) { - return r->test_compliance(target); + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], prefer_sba); + if (prefer_sba) { + /* Use system bus with highest priority */ + r->mem_access_methods[0] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; } else { - LOG_ERROR("This target does not support this command (may implement an older version of the spec)."); - return ERROR_FAIL; + /* Use progbuf with highest priority */ + r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; } + + /* Reset warning flags */ + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + + return ERROR_OK; } -COMMAND_HANDLER(riscv_set_prefer_sba) +COMMAND_HANDLER(riscv_set_mem_access) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + int progbuf_cnt = 0; + int sysbus_cnt = 0; + int abstract_cnt = 0; + + if (CMD_ARGC < 1 || CMD_ARGC > RISCV_NUM_MEM_ACCESS_METHODS) { + LOG_ERROR("Command takes 1 to %d parameters", RISCV_NUM_MEM_ACCESS_METHODS); return ERROR_COMMAND_SYNTAX_ERROR; } - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_prefer_sba); + + /* Check argument validity */ + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if (strcmp("progbuf", CMD_ARGV[i]) == 0) { + progbuf_cnt++; + } else if (strcmp("sysbus", CMD_ARGV[i]) == 0) { + sysbus_cnt++; + } else if (strcmp("abstract", CMD_ARGV[i]) == 0) { + abstract_cnt++; + } else { + LOG_ERROR("Unknown argument '%s'. " + "Must be one of: 'progbuf', 'sysbus' or 'abstract'.", CMD_ARGV[i]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + if (progbuf_cnt > 1 || sysbus_cnt > 1 || abstract_cnt > 1) { + LOG_ERROR("Syntax error - duplicate arguments to `riscv set_mem_access`."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* Args are valid, store them */ + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_UNSPECIFIED; + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if (strcmp("progbuf", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_PROGBUF; + else if (strcmp("sysbus", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_SYSBUS; + else if (strcmp("abstract", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_ABSTRACT; + } + + /* Reset warning flags */ + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + return ERROR_OK; } @@ -2257,109 +2450,180 @@ COMMAND_HANDLER(riscv_set_enable_virtual) return ERROR_OK; } -void parse_error(const char *string, char c, unsigned position) +int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val) { - char buf[position+2]; - for (unsigned i = 0; i < position; i++) - buf[i] = ' '; - buf[position] = '^'; - buf[position + 1] = 0; - - LOG_ERROR("Parse error at character %c in:", c); - LOG_ERROR("%s", string); - LOG_ERROR("%s", buf); -} + char *args = strdup(tcl_arg); + if (!args) + return ERROR_FAIL; -int parse_ranges(range_t **ranges, const char **argv) -{ - for (unsigned pass = 0; pass < 2; pass++) { - unsigned range = 0; + /* For backward compatibility, allow multiple parameters within one TCL argument, separated by ',' */ + char *arg = strtok(args, ","); + while (arg) { unsigned low = 0; - bool parse_low = true; unsigned high = 0; - for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) { - char c = argv[0][i]; - if (isspace(c)) { - /* Ignore whitespace. */ - continue; + char *name = NULL; + + char *dash = strchr(arg, '-'); + char *equals = strchr(arg, '='); + unsigned int pos; + + if (!dash && !equals) { + /* Expecting single register number. */ + if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) { + LOG_ERROR("Failed to parse single register number from '%s'.", arg); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } else if (dash && !equals) { + /* Expecting register range - two numbers separated by a dash: ##-## */ + *dash = 0; + dash++; + if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) { + LOG_ERROR("Failed to parse single register number from '%s'.", arg); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; + } + if (sscanf(dash, "%u%n", &high, &pos) != 1 || pos != strlen(dash)) { + LOG_ERROR("Failed to parse single register number from '%s'.", dash); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; + } + if (high < low) { + LOG_ERROR("Incorrect range encountered [%u, %u].", low, high); + free(args); + return ERROR_FAIL; + } + } else if (!dash && equals) { + /* Expecting single register number with textual name specified: ##=name */ + *equals = 0; + equals++; + if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) { + LOG_ERROR("Failed to parse single register number from '%s'.", arg); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; } - if (parse_low) { - if (isdigit(c)) { - low *= 10; - low += c - '0'; - } else if (c == '-') { - parse_low = false; - } else if (c == ',' || c == 0) { - if (pass == 1) { - (*ranges)[range].low = low; - (*ranges)[range].high = low; - } - low = 0; - range++; - } else { - parse_error(argv[0], c, i); - return ERROR_COMMAND_SYNTAX_ERROR; - } + name = calloc(1, strlen(equals) + strlen(reg_type) + 2); + if (!name) { + LOG_ERROR("Failed to allocate register name."); + free(args); + return ERROR_FAIL; + } - } else { - if (isdigit(c)) { - high *= 10; - high += c - '0'; - } else if (c == ',' || c == 0) { - parse_low = true; - if (pass == 1) { - (*ranges)[range].low = low; - (*ranges)[range].high = high; - } - low = 0; - high = 0; - range++; - } else { - parse_error(argv[0], c, i); - return ERROR_COMMAND_SYNTAX_ERROR; - } + /* Register prefix: "csr_" or "custom_" */ + strcpy(name, reg_type); + name[strlen(reg_type)] = '_'; + + if (sscanf(equals, "%[_a-zA-Z0-9]%n", name + strlen(reg_type) + 1, &pos) != 1 || pos != strlen(equals)) { + LOG_ERROR("Failed to parse register name from '%s'.", equals); + free(args); + free(name); + return ERROR_COMMAND_SYNTAX_ERROR; } + } else { + LOG_ERROR("Invalid argument '%s'.", arg); + free(args); + return ERROR_COMMAND_SYNTAX_ERROR; } - if (pass == 0) { - free(*ranges); - *ranges = calloc(range + 2, sizeof(range_t)); - if (!*ranges) + high = high > low ? high : low; + + if (high > max_val) { + LOG_ERROR("Cannot expose %s register number %u, maximum allowed value is %u.", reg_type, high, max_val); + free(name); + free(args); + return ERROR_FAIL; + } + + /* Check for overlap, name uniqueness. */ + range_list_t *entry; + list_for_each_entry(entry, ranges, list) { + if ((entry->low <= high) && (low <= entry->high)) { + if (low == high) + LOG_WARNING("Duplicate %s register number - " + "Register %u has already been exposed previously", reg_type, low); + else + LOG_WARNING("Overlapping register ranges - Register range starting from %u overlaps " + "with already exposed register/range at %u.", low, entry->low); + } + + if (entry->name && name && (strcasecmp(entry->name, name) == 0)) { + LOG_ERROR("Duplicate register name \"%s\" found.", name); + free(name); + free(args); return ERROR_FAIL; - } else { - (*ranges)[range].low = 1; - (*ranges)[range].high = 0; + } + } + + range_list_t *range = calloc(1, sizeof(range_list_t)); + if (!range) { + LOG_ERROR("Failed to allocate range list."); + free(name); + free(args); + return ERROR_FAIL; } + + range->low = low; + range->high = high; + range->name = name; + list_add(&range->list, ranges); + + arg = strtok(NULL, ","); } + free(args); return ERROR_OK; } COMMAND_HANDLER(riscv_set_expose_csrs) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + if (CMD_ARGC == 0) { + LOG_ERROR("Command expects parameters"); return ERROR_COMMAND_SYNTAX_ERROR; } - return parse_ranges(&expose_csr, CMD_ARGV); + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(info); + int ret = ERROR_OK; + + for (unsigned int i = 0; i < CMD_ARGC; i++) { + ret = parse_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff); + if (ret != ERROR_OK) + break; + } + + return ret; } COMMAND_HANDLER(riscv_set_expose_custom) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 parameter"); + if (CMD_ARGC == 0) { + LOG_ERROR("Command expects parameters"); return ERROR_COMMAND_SYNTAX_ERROR; } - return parse_ranges(&expose_custom, CMD_ARGV); + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(info); + int ret = ERROR_OK; + + for (unsigned int i = 0; i < CMD_ARGC; i++) { + ret = parse_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff); + if (ret != ERROR_OK) + break; + } + + return ret; } COMMAND_HANDLER(riscv_authdata_read) { - if (CMD_ARGC != 0) { - LOG_ERROR("Command takes no parameters"); + unsigned int index = 0; + if (CMD_ARGC == 0) { + /* nop */ + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index); + } else { + LOG_ERROR("Command takes at most one parameter"); return ERROR_COMMAND_SYNTAX_ERROR; } @@ -2377,7 +2641,7 @@ COMMAND_HANDLER(riscv_authdata_read) if (r->authdata_read) { uint32_t value; - if (r->authdata_read(target, &value) != ERROR_OK) + if (r->authdata_read(target, &value, index) != ERROR_OK) return ERROR_FAIL; command_print_sameline(CMD, "0x%08" PRIx32, value); return ERROR_OK; @@ -2389,19 +2653,26 @@ COMMAND_HANDLER(riscv_authdata_read) COMMAND_HANDLER(riscv_authdata_write) { - if (CMD_ARGC != 1) { - LOG_ERROR("Command takes exactly 1 argument"); + uint32_t value; + unsigned int index = 0; + + if (CMD_ARGC == 0) { + /* nop */ + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value); + } else if (CMD_ARGC == 2) { + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + } else { + LOG_ERROR("Command takes at most 2 arguments"); return ERROR_COMMAND_SYNTAX_ERROR; } struct target *target = get_current_target(CMD_CTX); RISCV_INFO(r); - uint32_t value; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value); - if (r->authdata_write) { - return r->authdata_write(target, value); + return r->authdata_write(target, value, index); } else { LOG_ERROR("authdata_write is not implemented for this target."); return ERROR_FAIL; @@ -2617,13 +2888,40 @@ COMMAND_HANDLER(riscv_set_ebreaku) return ERROR_OK; } +COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key, + unsigned int value) +{ + char full_key[80]; + snprintf(full_key, sizeof(full_key), "%s.%s", section, key); + command_print(CMD, "%-21s %3d", full_key, value); + return 0; +} + +COMMAND_HANDLER(handle_info) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + /* This output format can be fed directly into TCL's "array set". */ + + riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target)); + riscv_enumerate_triggers(target); + riscv_print_info_line(CMD, "hart", "trigger_count", + r->trigger_count); + + if (r->print_info) + return CALL_COMMAND_HANDLER(r->print_info, target); + + return 0; +} + static const struct command_registration riscv_exec_command_handlers[] = { { - .name = "test_compliance", - .handler = riscv_test_compliance, - .usage = "", + .name = "info", + .handler = handle_info, .mode = COMMAND_EXEC, - .help = "Runs a basic compliance test suite against the RISC-V Debug Spec." + .usage = "", + .help = "Displays some information OpenOCD detected about the target." }, { .name = "set_command_timeout_sec", @@ -2647,6 +2945,14 @@ static const struct command_registration riscv_exec_command_handlers[] = { .help = "When on, prefer to use System Bus Access to access memory. " "When off (default), prefer to use the Program Buffer to access memory." }, + { + .name = "set_mem_access", + .handler = riscv_set_mem_access, + .mode = COMMAND_ANY, + .usage = "method1 [method2] [method3]", + .help = "Set which memory access methods shall be used and in which order " + "of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'." + }, { .name = "set_enable_virtual", .handler = riscv_set_enable_virtual, @@ -2659,8 +2965,8 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "expose_csrs", .handler = riscv_set_expose_csrs, - .mode = COMMAND_ANY, - .usage = "n0[-m0][,n1[-m1]]...", + .mode = COMMAND_CONFIG, + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", .help = "Configure a list of inclusive ranges for CSRs to expose in " "addition to the standard ones. This must be executed before " "`init`." @@ -2668,8 +2974,8 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "expose_custom", .handler = riscv_set_expose_custom, - .mode = COMMAND_ANY, - .usage = "n0[-m0][,n1[-m1]]...", + .mode = COMMAND_CONFIG, + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", .help = "Configure a list of inclusive ranges for custom registers to " "expose. custom0 is accessed as abstract register number 0xc000, " "etc. This must be executed before `init`." @@ -2677,16 +2983,18 @@ static const struct command_registration riscv_exec_command_handlers[] = { { .name = "authdata_read", .handler = riscv_authdata_read, - .usage = "", + .usage = "[index]", .mode = COMMAND_ANY, - .help = "Return the 32-bit value read from authdata." + .help = "Return the 32-bit value read from authdata or authdata0 " + "(index=0), or authdata1 (index=1)." }, { .name = "authdata_write", .handler = riscv_authdata_write, .mode = COMMAND_ANY, - .usage = "value", - .help = "Write the 32-bit value to authdata." + .usage = "[index] value", + .help = "Write the 32-bit value to authdata or authdata0 (index=0), " + "or authdata1 (index=1)." }, { .name = "dmi_read", @@ -2836,6 +3144,7 @@ static unsigned int riscv_data_bits(struct target *target) struct target_type riscv_target = { .name = "riscv", + .target_create = riscv_create_target, .init_target = riscv_init_target, .deinit_target = riscv_deinit_target, .examine = riscv_examine, @@ -2889,49 +3198,37 @@ void riscv_info_init(struct target *target, riscv_info_t *r) r->dtm_version = 1; r->registers_initialized = false; r->current_hartid = target->coreid; + r->version_specific = NULL; memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id)); - for (size_t h = 0; h < RISCV_MAX_HARTS; ++h) - r->xlen[h] = -1; + r->xlen = -1; + + r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; + + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + + INIT_LIST_HEAD(&r->expose_csr); + INIT_LIST_HEAD(&r->expose_custom); } static int riscv_resume_go_all_harts(struct target *target) { RISCV_INFO(r); - /* Dummy variables to make mingw32-gcc happy. */ - int first = 0; - int last = 1; - int step = 1; - switch (resume_order) { - case RO_NORMAL: - first = 0; - last = riscv_count_harts(target) - 1; - step = 1; - break; - case RO_REVERSED: - first = riscv_count_harts(target) - 1; - last = 0; - step = -1; - break; - default: - assert(0); - } - - for (int i = first; i != last + step; i += step) { - if (!riscv_hart_enabled(target, i)) - continue; - - LOG_DEBUG("resuming hart %d", i); - if (riscv_set_current_hartid(target, i) != ERROR_OK) + LOG_DEBUG("[%s] resuming hart", target_name(target)); + if (riscv_select_current_hart(target) != ERROR_OK) + return ERROR_FAIL; + if (riscv_is_halted(target)) { + if (r->resume_go(target) != ERROR_OK) return ERROR_FAIL; - if (riscv_is_halted(target)) { - if (r->resume_go(target) != ERROR_OK) - return ERROR_FAIL; - } else { - LOG_DEBUG(" hart %d requested resume, but was already resumed", i); - } + } else { + LOG_DEBUG("[%s] hart requested resume, but was already resumed", + target_name(target)); } riscv_invalidate_register_cache(target); @@ -2941,17 +3238,9 @@ static int riscv_resume_go_all_harts(struct target *target) int riscv_step_rtos_hart(struct target *target) { RISCV_INFO(r); - int hartid = r->current_hartid; - if (riscv_rtos_enabled(target)) { - hartid = r->rtos_hartid; - if (hartid == -1) { - LOG_DEBUG("GDB has asked me to step \"any\" thread, so I'm stepping hart 0."); - hartid = 0; - } - } - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) + if (riscv_select_current_hart(target) != ERROR_OK) return ERROR_FAIL; - LOG_DEBUG("stepping hart %d", hartid); + LOG_DEBUG("[%s] stepping", target_name(target)); if (!riscv_is_halted(target)) { LOG_ERROR("Hart isn't halted before single step!"); @@ -2970,7 +3259,7 @@ int riscv_step_rtos_hart(struct target *target) return ERROR_OK; } -bool riscv_supports_extension(struct target *target, int hartid, char letter) +bool riscv_supports_extension(struct target *target, char letter) { RISCV_INFO(r); unsigned num; @@ -2980,24 +3269,13 @@ bool riscv_supports_extension(struct target *target, int hartid, char letter) num = letter - 'A'; else return false; - return r->misa[hartid] & (1 << num); + return r->misa & BIT(num); } unsigned riscv_xlen(const struct target *target) -{ - return riscv_xlen_of_hart(target, riscv_current_hartid(target)); -} - -int riscv_xlen_of_hart(const struct target *target, int hartid) { RISCV_INFO(r); - assert(r->xlen[hartid] != -1); - return r->xlen[hartid]; -} - -bool riscv_rtos_enabled(const struct target *target) -{ - return false; + return r->xlen; } int riscv_set_current_hartid(struct target *target, int hartid) @@ -3008,16 +3286,10 @@ int riscv_set_current_hartid(struct target *target, int hartid) int previous_hartid = riscv_current_hartid(target); r->current_hartid = hartid; - assert(riscv_hart_enabled(target, hartid)); LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid); if (r->select_current_hart(target) != ERROR_OK) return ERROR_FAIL; - /* This might get called during init, in which case we shouldn't be - * setting up the register cache. */ - if (target_was_examined(target) && riscv_rtos_enabled(target)) - riscv_invalidate_register_cache(target); - return ERROR_OK; } @@ -3041,19 +3313,6 @@ int riscv_current_hartid(const struct target *target) return r->current_hartid; } -void riscv_set_all_rtos_harts(struct target *target) -{ - RISCV_INFO(r); - r->rtos_hartid = -1; -} - -void riscv_set_rtos_hartid(struct target *target, int hartid) -{ - LOG_DEBUG("setting RTOS hartid %d", hartid); - RISCV_INFO(r); - r->rtos_hartid = hartid; -} - int riscv_count_harts(struct target *target) { if (!target) @@ -3064,11 +3323,6 @@ int riscv_count_harts(struct target *target) return r->hart_count(target); } -bool riscv_has_register(struct target *target, int hartid, int regid) -{ - return 1; -} - /** * If write is true: * return true iff we are guaranteed that the register will contain exactly @@ -3085,7 +3339,7 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write) (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)) return true; - /* Most CSRs won't change value on us, but we can't assume it about rbitrary + /* Most CSRs won't change value on us, but we can't assume it about arbitrary * CSRs. */ switch (regno) { case GDB_REGNO_DPC: @@ -3122,75 +3376,67 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write) * This function is called when the debug user wants to change the value of a * register. The new value may be cached, and may not be written until the hart * is resumed. */ -int riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v) -{ - return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v); -} - -int riscv_set_register_on_hart(struct target *target, int hartid, - enum gdb_regno regid, uint64_t value) +int riscv_set_register(struct target *target, enum gdb_regno regid, riscv_reg_t value) { RISCV_INFO(r); - LOG_DEBUG("{%d} %s <- %" PRIx64, hartid, gdb_regno_name(regid), value); + LOG_DEBUG("[%s] %s <- %" PRIx64, target_name(target), gdb_regno_name(regid), value); assert(r->set_register); + keep_alive(); + /* TODO: Hack to deal with gdb that thinks these registers still exist. */ if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 && - riscv_supports_extension(target, hartid, 'E')) + riscv_supports_extension(target, 'E')) return ERROR_OK; struct reg *reg = &target->reg_cache->reg_list[regid]; buf_set_u64(reg->value, 0, reg->size, value); - int result = r->set_register(target, hartid, regid, value); + int result = r->set_register(target, regid, value); if (result == ERROR_OK) reg->valid = gdb_regno_cacheable(regid, true); else reg->valid = false; - LOG_DEBUG("[%s]{%d} wrote 0x%" PRIx64 " to %s valid=%d", - target_name(target), hartid, value, reg->name, reg->valid); + LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d", + target_name(target), value, reg->name, reg->valid); return result; } int riscv_get_register(struct target *target, riscv_reg_t *value, - enum gdb_regno r) -{ - return riscv_get_register_on_hart(target, value, - riscv_current_hartid(target), r); -} - -int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value, - int hartid, enum gdb_regno regid) + enum gdb_regno regid) { RISCV_INFO(r); + keep_alive(); + struct reg *reg = &target->reg_cache->reg_list[regid]; if (!reg->exist) { - LOG_DEBUG("[%s]{%d} %s does not exist.", - target_name(target), hartid, gdb_regno_name(regid)); + LOG_DEBUG("[%s] %s does not exist.", + target_name(target), gdb_regno_name(regid)); return ERROR_FAIL; } - if (reg && reg->valid && hartid == riscv_current_hartid(target)) { + if (reg && reg->valid) { *value = buf_get_u64(reg->value, 0, reg->size); - LOG_DEBUG("{%d} %s: %" PRIx64 " (cached)", hartid, + LOG_DEBUG("[%s] %s: %" PRIx64 " (cached)", target_name(target), gdb_regno_name(regid), *value); return ERROR_OK; } /* TODO: Hack to deal with gdb that thinks these registers still exist. */ if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && - riscv_supports_extension(target, hartid, 'E')) { + riscv_supports_extension(target, 'E')) { *value = 0; return ERROR_OK; } - int result = r->get_register(target, value, hartid, regid); + int result = r->get_register(target, value, regid); if (result == ERROR_OK) reg->valid = gdb_regno_cacheable(regid, false); - LOG_DEBUG("{%d} %s: %" PRIx64, hartid, gdb_regno_name(regid), *value); + LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target), + gdb_regno_name(regid), *value); return result; } @@ -3216,7 +3462,7 @@ enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid) size_t riscv_debug_buffer_size(struct target *target) { RISCV_INFO(r); - return r->debug_buffer_size[riscv_current_hartid(target)]; + return r->debug_buffer_size; } int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn) @@ -3262,15 +3508,6 @@ int riscv_dmi_write_u64_bits(struct target *target) return r->dmi_write_u64_bits(target); } -bool riscv_hart_enabled(struct target *target, int hartid) -{ - /* FIXME: Add a hart mask to the RTOS. */ - if (riscv_rtos_enabled(target)) - return hartid < riscv_count_harts(target); - - return hartid == target->coreid; -} - /** * Count triggers, and initialize trigger_count for each hart. * trigger_count is initialized even if this function fails to discover @@ -3287,58 +3524,57 @@ int riscv_enumerate_triggers(struct target *target) r->triggers_enumerated = true; /* At the very least we tried. */ - for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) { - if (!riscv_hart_enabled(target, hartid)) - continue; + riscv_reg_t tselect; + int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT); + /* If tselect is not readable, the trigger module is likely not + * implemented. There are no triggers to enumerate then and no error + * should be thrown. */ + if (result != ERROR_OK) { + LOG_DEBUG("[%s] Cannot access tselect register. " + "Assuming that triggers are not implemented.", target_name(target)); + r->trigger_count = 0; + return ERROR_OK; + } - riscv_reg_t tselect; - int result = riscv_get_register_on_hart(target, &tselect, hartid, - GDB_REGNO_TSELECT); + for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) { + r->trigger_count = t; + + /* If we can't write tselect, then this hart does not support triggers. */ + if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) + break; + uint64_t tselect_rb; + result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT); + if (result != ERROR_OK) + return result; + /* Mask off the top bit, which is used as tdrmode in old + * implementations. */ + tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1)); + if (tselect_rb != t) + break; + uint64_t tdata1; + result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1); if (result != ERROR_OK) return result; - for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) { - r->trigger_count[hartid] = t; - - /* If we can't write tselect, then this hart does not support triggers. */ - if (riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t) != ERROR_OK) - break; - uint64_t tselect_rb; - result = riscv_get_register_on_hart(target, &tselect_rb, hartid, - GDB_REGNO_TSELECT); - if (result != ERROR_OK) - return result; - /* Mask off the top bit, which is used as tdrmode in old - * implementations. */ - tselect_rb &= ~(1ULL << (riscv_xlen(target)-1)); - if (tselect_rb != t) + int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); + if (type == 0) + break; + switch (type) { + case 1: + /* On these older cores we don't support software using + * triggers. */ + riscv_set_register(target, GDB_REGNO_TDATA1, 0); break; - uint64_t tdata1; - result = riscv_get_register_on_hart(target, &tdata1, hartid, - GDB_REGNO_TDATA1); - if (result != ERROR_OK) - return result; - - int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target))); - if (type == 0) + case 2: + if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) + riscv_set_register(target, GDB_REGNO_TDATA1, 0); break; - switch (type) { - case 1: - /* On these older cores we don't support software using - * triggers. */ - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); - break; - case 2: - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0); - break; - } } + } - riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect); + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); - LOG_INFO("[%d] Found %d triggers", hartid, r->trigger_count[hartid]); - } + LOG_INFO("[%s] Found %d triggers", target_name(target), r->trigger_count); return ERROR_OK; } @@ -3549,8 +3785,8 @@ static int register_get(struct reg *reg) } reg->valid = gdb_regno_cacheable(reg->number, false); char *str = buf_to_hex_str(reg->value, reg->size); - LOG_DEBUG("[%d]{%d} read 0x%s from %s (valid=%d)", target->coreid, - riscv_current_hartid(target), str, reg->name, reg->valid); + LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target), + str, reg->name, reg->valid); free(str); return ERROR_OK; } @@ -3562,10 +3798,15 @@ static int register_set(struct reg *reg, uint8_t *buf) RISCV_INFO(r); char *str = buf_to_hex_str(buf, reg->size); - LOG_DEBUG("[%d]{%d} write 0x%s to %s (valid=%d)", target->coreid, - riscv_current_hartid(target), str, reg->name, reg->valid); + LOG_DEBUG("[%s] write 0x%s to %s (valid=%d)", target_name(target), + str, reg->name, reg->valid); free(str); + /* Exit early for writing x0, which on the hardware would be ignored, and we + * don't want to update our cache. */ + if (reg->number == GDB_REGNO_ZERO) + return ERROR_OK; + memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8)); reg->valid = gdb_regno_cacheable(reg->number, true); @@ -3625,13 +3866,10 @@ int riscv_init_registers(struct target *target) target->reg_cache->name = "RISC-V Registers"; target->reg_cache->num_regs = GDB_REGNO_COUNT; - if (expose_custom) { - for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) { - for (unsigned number = expose_custom[i].low; - number <= expose_custom[i].high; - number++) - target->reg_cache->num_regs++; - } + if (!list_empty(&info->expose_custom)) { + range_list_t *entry; + list_for_each_entry(entry, &info->expose_custom, list) + target->reg_cache->num_regs += entry->high - entry->low + 1; } LOG_DEBUG("create register cache for %d registers", @@ -3650,8 +3888,6 @@ int riscv_init_registers(struct target *target) return ERROR_FAIL; char *reg_name = info->reg_names; - int hartid = riscv_current_hartid(target); - static struct reg_feature feature_cpu = { .name = "org.gnu.gdb.riscv.cpu" }; @@ -3709,35 +3945,35 @@ int riscv_init_registers(struct target *target) */ info->vector_uint8.type = &type_uint8; - info->vector_uint8.count = info->vlenb[hartid]; + info->vector_uint8.count = info->vlenb; info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint8_vector.id = "bytes"; info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint8_vector.reg_type_vector = &info->vector_uint8; info->vector_uint16.type = &type_uint16; - info->vector_uint16.count = info->vlenb[hartid] / 2; + info->vector_uint16.count = info->vlenb / 2; info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint16_vector.id = "shorts"; info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint16_vector.reg_type_vector = &info->vector_uint16; info->vector_uint32.type = &type_uint32; - info->vector_uint32.count = info->vlenb[hartid] / 4; + info->vector_uint32.count = info->vlenb / 4; info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint32_vector.id = "words"; info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint32_vector.reg_type_vector = &info->vector_uint32; info->vector_uint64.type = &type_uint64; - info->vector_uint64.count = info->vlenb[hartid] / 8; + info->vector_uint64.count = info->vlenb / 8; info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint64_vector.id = "longs"; info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR; info->type_uint64_vector.reg_type_vector = &info->vector_uint64; info->vector_uint128.type = &type_uint128; - info->vector_uint128.count = info->vlenb[hartid] / 16; + info->vector_uint128.count = info->vlenb / 16; info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED; info->type_uint128_vector.id = "quads"; info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR; @@ -3745,28 +3981,28 @@ int riscv_init_registers(struct target *target) info->vector_fields[0].name = "b"; info->vector_fields[0].type = &info->type_uint8_vector; - if (info->vlenb[hartid] >= 2) { + if (info->vlenb >= 2) { info->vector_fields[0].next = info->vector_fields + 1; info->vector_fields[1].name = "s"; info->vector_fields[1].type = &info->type_uint16_vector; } else { info->vector_fields[0].next = NULL; } - if (info->vlenb[hartid] >= 4) { + if (info->vlenb >= 4) { info->vector_fields[1].next = info->vector_fields + 2; info->vector_fields[2].name = "w"; info->vector_fields[2].type = &info->type_uint32_vector; } else { info->vector_fields[1].next = NULL; } - if (info->vlenb[hartid] >= 8) { + if (info->vlenb >= 8) { info->vector_fields[2].next = info->vector_fields + 3; info->vector_fields[3].name = "l"; info->vector_fields[3].type = &info->type_uint64_vector; } else { info->vector_fields[2].next = NULL; } - if (info->vlenb[hartid] >= 16) { + if (info->vlenb >= 16) { info->vector_fields[3].next = info->vector_fields + 4; info->vector_fields[4].name = "q"; info->vector_fields[4].type = &info->type_uint128_vector; @@ -3791,7 +4027,6 @@ int riscv_init_registers(struct target *target) qsort(csr_info, ARRAY_SIZE(csr_info), sizeof(*csr_info), cmp_csr_info); unsigned csr_info_index = 0; - unsigned custom_range_index = 0; int custom_within_range = 0; riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t)); @@ -3818,7 +4053,7 @@ int riscv_init_registers(struct target *target) * of other things to break in that case as well. */ if (number <= GDB_REGNO_XPR31) { r->exist = number <= GDB_REGNO_XPR15 || - !riscv_supports_extension(target, hartid, 'E'); + !riscv_supports_extension(target, 'E'); /* TODO: For now we fake that all GPRs exist because otherwise gdb * doesn't work. */ r->exist = true; @@ -3930,13 +4165,13 @@ int riscv_init_registers(struct target *target) r->feature = &feature_cpu; } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { r->caller_save = true; - if (riscv_supports_extension(target, hartid, 'D')) { + if (riscv_supports_extension(target, 'D')) { r->size = 64; - if (riscv_supports_extension(target, hartid, 'F')) + if (riscv_supports_extension(target, 'F')) r->reg_data_type = &type_ieee_single_double; else r->reg_data_type = &type_ieee_double; - } else if (riscv_supports_extension(target, hartid, 'F')) { + } else if (riscv_supports_extension(target, 'F')) { r->reg_data_type = &type_ieee_single; r->size = 32; } else { @@ -4067,7 +4302,7 @@ int riscv_init_registers(struct target *target) case CSR_FFLAGS: case CSR_FRM: case CSR_FCSR: - r->exist = riscv_supports_extension(target, hartid, 'F'); + r->exist = riscv_supports_extension(target, 'F'); r->group = "float"; r->feature = &feature_fpu; break; @@ -4081,15 +4316,15 @@ int riscv_init_registers(struct target *target) case CSR_SCAUSE: case CSR_STVAL: case CSR_SATP: - r->exist = riscv_supports_extension(target, hartid, 'S'); + r->exist = riscv_supports_extension(target, 'S'); break; case CSR_MEDELEG: case CSR_MIDELEG: /* "In systems with only M-mode, or with both M-mode and * U-mode but without U-mode trap support, the medeleg and * mideleg registers should not exist." */ - r->exist = riscv_supports_extension(target, hartid, 'S') || - riscv_supports_extension(target, hartid, 'N'); + r->exist = riscv_supports_extension(target, 'S') || + riscv_supports_extension(target, 'N'); break; case CSR_PMPCFG1: @@ -4166,18 +4401,25 @@ int riscv_init_registers(struct target *target) case CSR_VL: case CSR_VTYPE: case CSR_VLENB: - r->exist = riscv_supports_extension(target, hartid, 'V'); + r->exist = riscv_supports_extension(target, 'V'); break; } - if (!r->exist && expose_csr) { - for (unsigned i = 0; expose_csr[i].low <= expose_csr[i].high; i++) { - if (csr_number >= expose_csr[i].low && csr_number <= expose_csr[i].high) { - LOG_INFO("Exposing additional CSR %d", csr_number); + if (!r->exist && !list_empty(&info->expose_csr)) { + range_list_t *entry; + list_for_each_entry(entry, &info->expose_csr, list) + if ((entry->low <= csr_number) && (csr_number <= entry->high)) { + if (entry->name) { + *reg_name = 0; + r->name = entry->name; + } + + LOG_DEBUG("Exposing additional CSR %d (name=%s)", + csr_number, entry->name ? entry->name : reg_name); + r->exist = true; break; } - } } } else if (number == GDB_REGNO_PRIV) { @@ -4188,8 +4430,8 @@ int riscv_init_registers(struct target *target) } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) { r->caller_save = false; - r->exist = riscv_supports_extension(target, hartid, 'V') && info->vlenb[hartid]; - r->size = info->vlenb[hartid] * 8; + r->exist = riscv_supports_extension(target, 'V') && info->vlenb; + r->size = info->vlenb * 8; sprintf(reg_name, "v%d", number - GDB_REGNO_V0); r->group = "vector"; r->feature = &feature_vector; @@ -4197,10 +4439,10 @@ int riscv_init_registers(struct target *target) } else if (number >= GDB_REGNO_COUNT) { /* Custom registers. */ - assert(expose_custom); + assert(!list_empty(&info->expose_custom)); + + range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list); - range_t *range = &expose_custom[custom_range_index]; - assert(range->low <= range->high); unsigned custom_number = range->low + custom_within_range; r->group = "custom"; @@ -4212,18 +4454,27 @@ int riscv_init_registers(struct target *target) ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number; sprintf(reg_name, "custom%d", custom_number); + if (range->name) { + *reg_name = 0; + r->name = range->name; + } + + LOG_DEBUG("Exposing additional custom register %d (name=%s)", + number, range->name ? range->name : reg_name); + custom_within_range++; if (custom_within_range > range->high - range->low) { custom_within_range = 0; - custom_range_index++; + list_rotate_left(&info->expose_custom); } } - if (reg_name[0]) + if (reg_name[0]) { r->name = reg_name; - reg_name += strlen(reg_name) + 1; - assert(reg_name < info->reg_names + target->reg_cache->num_regs * - max_reg_name_len); + reg_name += strlen(reg_name) + 1; + assert(reg_name < info->reg_names + target->reg_cache->num_regs * + max_reg_name_len); + } r->value = info->reg_cache_values[number]; } diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index d943134e23..d0f4f6ec04 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 0072b9afe7..b347212d34 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) {