1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2009 by David Brownell
13 #include "armv8_dpm.h"
14 #include <jtag/jtag.h>
16 #include "breakpoints.h"
17 #include "target_type.h"
18 #include "armv8_opcodes.h"
20 #include "helper/time_support.h"
23 #define T32_FMTITR(instr) (((instr & 0x0000FFFF) << 16) | ((instr & 0xFFFF0000) >> 16))
27 * Implements various ARM DPM operations using architectural debug registers.
28 * These routines layer over core-specific communication methods to cope with
29 * implementation differences between cores like ARM1136 and Cortex-A8.
31 * The "Debug Programmers' Model" (DPM) for ARMv6 and ARMv7 is defined by
32 * Part C (Debug Architecture) of the ARM Architecture Reference Manual,
33 * ARMv7-A and ARMv7-R edition (ARM DDI 0406B). In OpenOCD, DPM operations
34 * are abstracted through internal programming interfaces to share code and
35 * to minimize needless differences in debug behavior between cores.
39 * Get core state from EDSCR, without necessity to retrieve CPSR
41 enum arm_state
armv8_dpm_get_core_state(struct arm_dpm
*dpm
)
43 int el
= (dpm
->dscr
>> 8) & 0x3;
44 int rw
= (dpm
->dscr
>> 10) & 0xF;
48 /* In Debug state, each bit gives the current Execution state of each EL */
50 return ARM_STATE_AARCH64
;
55 /*----------------------------------------------------------------------*/
57 static int dpmv8_write_dcc(struct armv8_common
*armv8
, uint32_t data
)
59 return mem_ap_write_u32(armv8
->debug_ap
,
60 armv8
->debug_base
+ CPUV8_DBG_DTRRX
, data
);
63 static int dpmv8_write_dcc_64(struct armv8_common
*armv8
, uint64_t data
)
66 ret
= mem_ap_write_u32(armv8
->debug_ap
,
67 armv8
->debug_base
+ CPUV8_DBG_DTRRX
, data
);
69 ret
= mem_ap_write_u32(armv8
->debug_ap
,
70 armv8
->debug_base
+ CPUV8_DBG_DTRTX
, data
>> 32);
74 static int dpmv8_read_dcc(struct armv8_common
*armv8
, uint32_t *data
,
77 uint32_t dscr
= DSCR_ITE
;
83 /* Wait for DTRRXfull */
84 long long then
= timeval_ms();
85 while ((dscr
& DSCR_DTR_TX_FULL
) == 0) {
86 retval
= mem_ap_read_atomic_u32(armv8
->debug_ap
,
87 armv8
->debug_base
+ CPUV8_DBG_DSCR
,
89 if (retval
!= ERROR_OK
)
91 if (timeval_ms() > then
+ 1000) {
92 LOG_ERROR("Timeout waiting for read dcc");
97 retval
= mem_ap_read_atomic_u32(armv8
->debug_ap
,
98 armv8
->debug_base
+ CPUV8_DBG_DTRTX
,
100 if (retval
!= ERROR_OK
)
109 static int dpmv8_read_dcc_64(struct armv8_common
*armv8
, uint64_t *data
,
112 uint32_t dscr
= DSCR_ITE
;
119 /* Wait for DTRRXfull */
120 long long then
= timeval_ms();
121 while ((dscr
& DSCR_DTR_TX_FULL
) == 0) {
122 retval
= mem_ap_read_atomic_u32(armv8
->debug_ap
,
123 armv8
->debug_base
+ CPUV8_DBG_DSCR
,
125 if (retval
!= ERROR_OK
)
127 if (timeval_ms() > then
+ 1000) {
128 LOG_ERROR("Timeout waiting for DTR_TX_FULL, dscr = 0x%08" PRIx32
, dscr
);
133 retval
= mem_ap_read_atomic_u32(armv8
->debug_ap
,
134 armv8
->debug_base
+ CPUV8_DBG_DTRTX
,
136 if (retval
!= ERROR_OK
)
139 retval
= mem_ap_read_atomic_u32(armv8
->debug_ap
,
140 armv8
->debug_base
+ CPUV8_DBG_DTRRX
,
142 if (retval
!= ERROR_OK
)
145 *data
= *(uint32_t *)data
| (uint64_t)higher
<< 32;
153 static int dpmv8_dpm_prepare(struct arm_dpm
*dpm
)
155 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
159 /* set up invariant: ITE is set after ever DPM operation */
160 long long then
= timeval_ms();
162 retval
= mem_ap_read_atomic_u32(armv8
->debug_ap
,
163 armv8
->debug_base
+ CPUV8_DBG_DSCR
,
165 if (retval
!= ERROR_OK
)
167 if ((dscr
& DSCR_ITE
) != 0)
169 if (timeval_ms() > then
+ 1000) {
170 LOG_ERROR("Timeout waiting for dpm prepare");
175 /* update the stored copy of dscr */
178 /* this "should never happen" ... */
179 if (dscr
& DSCR_DTR_RX_FULL
) {
180 LOG_ERROR("DSCR_DTR_RX_FULL, dscr 0x%08" PRIx32
, dscr
);
182 retval
= mem_ap_read_u32(armv8
->debug_ap
,
183 armv8
->debug_base
+ CPUV8_DBG_DTRRX
, &dscr
);
184 if (retval
!= ERROR_OK
)
191 static int dpmv8_dpm_finish(struct arm_dpm
*dpm
)
193 /* REVISIT what could be done here? */
197 static int dpmv8_exec_opcode(struct arm_dpm
*dpm
,
198 uint32_t opcode
, uint32_t *p_dscr
)
200 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
201 uint32_t dscr
= dpm
->dscr
;
207 /* Wait for InstrCompl bit to be set */
208 long long then
= timeval_ms();
209 while ((dscr
& DSCR_ITE
) == 0) {
210 retval
= mem_ap_read_atomic_u32(armv8
->debug_ap
,
211 armv8
->debug_base
+ CPUV8_DBG_DSCR
, &dscr
);
212 if (retval
!= ERROR_OK
) {
213 LOG_ERROR("Could not read DSCR register, opcode = 0x%08" PRIx32
, opcode
);
216 if (timeval_ms() > then
+ 1000) {
217 LOG_ERROR("Timeout waiting for aarch64_exec_opcode");
222 if (armv8_dpm_get_core_state(dpm
) != ARM_STATE_AARCH64
)
223 opcode
= T32_FMTITR(opcode
);
225 retval
= mem_ap_write_u32(armv8
->debug_ap
,
226 armv8
->debug_base
+ CPUV8_DBG_ITR
, opcode
);
227 if (retval
!= ERROR_OK
)
232 retval
= mem_ap_read_atomic_u32(armv8
->debug_ap
,
233 armv8
->debug_base
+ CPUV8_DBG_DSCR
, &dscr
);
234 if (retval
!= ERROR_OK
) {
235 LOG_ERROR("Could not read DSCR register");
238 if (timeval_ms() > then
+ 1000) {
239 LOG_ERROR("Timeout waiting for aarch64_exec_opcode");
242 } while ((dscr
& DSCR_ITE
) == 0); /* Wait for InstrCompl bit to be set */
244 /* update dscr and el after each command execution */
246 if (dpm
->last_el
!= ((dscr
>> 8) & 3))
247 LOG_DEBUG("EL %i -> %" PRIu32
, dpm
->last_el
, (dscr
>> 8) & 3);
248 dpm
->last_el
= (dscr
>> 8) & 3;
250 if (dscr
& DSCR_ERR
) {
251 LOG_ERROR("Opcode 0x%08" PRIx32
", DSCR.ERR=1, DSCR.EL=%i", opcode
, dpm
->last_el
);
252 armv8_dpm_handle_exception(dpm
, true);
262 static int dpmv8_instr_execute(struct arm_dpm
*dpm
, uint32_t opcode
)
264 return dpmv8_exec_opcode(dpm
, opcode
, NULL
);
267 static int dpmv8_instr_write_data_dcc(struct arm_dpm
*dpm
,
268 uint32_t opcode
, uint32_t data
)
270 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
273 retval
= dpmv8_write_dcc(armv8
, data
);
274 if (retval
!= ERROR_OK
)
277 return dpmv8_exec_opcode(dpm
, opcode
, NULL
);
280 static int dpmv8_instr_write_data_dcc_64(struct arm_dpm
*dpm
,
281 uint32_t opcode
, uint64_t data
)
283 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
286 retval
= dpmv8_write_dcc_64(armv8
, data
);
287 if (retval
!= ERROR_OK
)
290 return dpmv8_exec_opcode(dpm
, opcode
, NULL
);
293 static int dpmv8_instr_write_data_r0(struct arm_dpm
*dpm
,
294 uint32_t opcode
, uint32_t data
)
296 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
297 uint32_t dscr
= DSCR_ITE
;
300 retval
= dpmv8_write_dcc(armv8
, data
);
301 if (retval
!= ERROR_OK
)
304 retval
= dpmv8_exec_opcode(dpm
, armv8_opcode(armv8
, READ_REG_DTRRX
), &dscr
);
305 if (retval
!= ERROR_OK
)
308 /* then the opcode, taking data from R0 */
309 return dpmv8_exec_opcode(dpm
, opcode
, &dscr
);
312 static int dpmv8_instr_write_data_r0_64(struct arm_dpm
*dpm
,
313 uint32_t opcode
, uint64_t data
)
315 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
318 if (dpm
->arm
->core_state
!= ARM_STATE_AARCH64
)
319 return dpmv8_instr_write_data_r0(dpm
, opcode
, data
);
321 /* transfer data from DCC to R0 */
322 retval
= dpmv8_write_dcc_64(armv8
, data
);
323 if (retval
== ERROR_OK
)
324 retval
= dpmv8_exec_opcode(dpm
, ARMV8_MRS(SYSTEM_DBG_DBGDTR_EL0
, 0), &dpm
->dscr
);
326 /* then the opcode, taking data from R0 */
327 if (retval
== ERROR_OK
)
328 retval
= dpmv8_exec_opcode(dpm
, opcode
, &dpm
->dscr
);
333 static int dpmv8_instr_cpsr_sync(struct arm_dpm
*dpm
)
336 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
338 /* "Prefetch flush" after modifying execution status in CPSR */
339 retval
= dpmv8_exec_opcode(dpm
, armv8_opcode(armv8
, ARMV8_OPC_DSB_SY
), &dpm
->dscr
);
340 if (retval
== ERROR_OK
)
341 dpmv8_exec_opcode(dpm
, armv8_opcode(armv8
, ARMV8_OPC_ISB_SY
), &dpm
->dscr
);
345 static int dpmv8_instr_read_data_dcc(struct arm_dpm
*dpm
,
346 uint32_t opcode
, uint32_t *data
)
348 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
351 /* the opcode, writing data to DCC */
352 retval
= dpmv8_exec_opcode(dpm
, opcode
, &dpm
->dscr
);
353 if (retval
!= ERROR_OK
)
356 return dpmv8_read_dcc(armv8
, data
, &dpm
->dscr
);
359 static int dpmv8_instr_read_data_dcc_64(struct arm_dpm
*dpm
,
360 uint32_t opcode
, uint64_t *data
)
362 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
365 /* the opcode, writing data to DCC */
366 retval
= dpmv8_exec_opcode(dpm
, opcode
, &dpm
->dscr
);
367 if (retval
!= ERROR_OK
)
370 return dpmv8_read_dcc_64(armv8
, data
, &dpm
->dscr
);
373 static int dpmv8_instr_read_data_r0(struct arm_dpm
*dpm
,
374 uint32_t opcode
, uint32_t *data
)
376 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
379 /* the opcode, writing data to R0 */
380 retval
= dpmv8_exec_opcode(dpm
, opcode
, &dpm
->dscr
);
381 if (retval
!= ERROR_OK
)
384 /* write R0 to DCC */
385 retval
= dpmv8_exec_opcode(dpm
, armv8_opcode(armv8
, WRITE_REG_DTRTX
), &dpm
->dscr
);
386 if (retval
!= ERROR_OK
)
389 return dpmv8_read_dcc(armv8
, data
, &dpm
->dscr
);
392 static int dpmv8_instr_read_data_r0_64(struct arm_dpm
*dpm
,
393 uint32_t opcode
, uint64_t *data
)
395 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
398 if (dpm
->arm
->core_state
!= ARM_STATE_AARCH64
) {
400 retval
= dpmv8_instr_read_data_r0(dpm
, opcode
, &tmp
);
401 if (retval
== ERROR_OK
)
406 /* the opcode, writing data to R0 */
407 retval
= dpmv8_exec_opcode(dpm
, opcode
, &dpm
->dscr
);
408 if (retval
!= ERROR_OK
)
411 /* write R0 to DCC */
412 retval
= dpmv8_exec_opcode(dpm
, ARMV8_MSR_GP(SYSTEM_DBG_DBGDTR_EL0
, 0), &dpm
->dscr
);
413 if (retval
!= ERROR_OK
)
416 return dpmv8_read_dcc_64(armv8
, data
, &dpm
->dscr
);
420 static int dpmv8_bpwp_enable(struct arm_dpm
*dpm
, unsigned index_t
,
421 target_addr_t addr
, uint32_t control
)
423 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
424 uint32_t vr
= armv8
->debug_base
;
425 uint32_t cr
= armv8
->debug_base
;
429 case 0 ... 15: /* breakpoints */
430 vr
+= CPUV8_DBG_BVR_BASE
;
431 cr
+= CPUV8_DBG_BCR_BASE
;
433 case 16 ... 31: /* watchpoints */
434 vr
+= CPUV8_DBG_WVR_BASE
;
435 cr
+= CPUV8_DBG_WCR_BASE
;
444 LOG_DEBUG("A8: bpwp enable, vr %08x cr %08x",
445 (unsigned) vr
, (unsigned) cr
);
447 retval
= mem_ap_write_atomic_u32(armv8
->debug_ap
, vr
, addr
);
448 if (retval
!= ERROR_OK
)
450 return mem_ap_write_atomic_u32(armv8
->debug_ap
, cr
, control
);
454 static int dpmv8_bpwp_disable(struct arm_dpm
*dpm
, unsigned index_t
)
456 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
461 cr
= armv8
->debug_base
+ CPUV8_DBG_BCR_BASE
;
464 cr
= armv8
->debug_base
+ CPUV8_DBG_WCR_BASE
;
472 LOG_DEBUG("A: bpwp disable, cr %08x", (unsigned) cr
);
474 /* clear control register */
475 return mem_ap_write_atomic_u32(armv8
->debug_ap
, cr
, 0);
479 * Coprocessor support
482 /* Read coprocessor */
483 static int dpmv8_mrc(struct target
*target
, int cpnum
,
484 uint32_t op1
, uint32_t op2
, uint32_t crn
, uint32_t crm
,
487 struct arm
*arm
= target_to_arm(target
);
488 struct arm_dpm
*dpm
= arm
->dpm
;
491 retval
= dpm
->prepare(dpm
);
492 if (retval
!= ERROR_OK
)
495 LOG_DEBUG("MRC p%d, %d, r0, c%d, c%d, %d", cpnum
,
496 (int) op1
, (int) crn
,
497 (int) crm
, (int) op2
);
499 /* read coprocessor register into R0; return via DCC */
500 retval
= dpm
->instr_read_data_r0(dpm
,
501 ARMV4_5_MRC(cpnum
, op1
, 0, crn
, crm
, op2
),
504 /* (void) */ dpm
->finish(dpm
);
508 static int dpmv8_mcr(struct target
*target
, int cpnum
,
509 uint32_t op1
, uint32_t op2
, uint32_t crn
, uint32_t crm
,
512 struct arm
*arm
= target_to_arm(target
);
513 struct arm_dpm
*dpm
= arm
->dpm
;
516 retval
= dpm
->prepare(dpm
);
517 if (retval
!= ERROR_OK
)
520 LOG_DEBUG("MCR p%d, %d, r0, c%d, c%d, %d", cpnum
,
521 (int) op1
, (int) crn
,
522 (int) crm
, (int) op2
);
524 /* read DCC into r0; then write coprocessor register from R0 */
525 retval
= dpm
->instr_write_data_r0(dpm
,
526 ARMV4_5_MCR(cpnum
, op1
, 0, crn
, crm
, op2
),
529 /* (void) */ dpm
->finish(dpm
);
533 /*----------------------------------------------------------------------*/
536 * Register access utilities
539 int armv8_dpm_modeswitch(struct arm_dpm
*dpm
, enum arm_mode mode
)
541 struct armv8_common
*armv8
= (struct armv8_common
*)dpm
->arm
->arch_info
;
542 int retval
= ERROR_OK
;
543 unsigned int target_el
;
544 enum arm_state core_state
;
547 /* restore previous mode */
548 if (mode
== ARM_MODE_ANY
) {
549 cpsr
= buf_get_u32(dpm
->arm
->cpsr
->value
, 0, 32);
551 LOG_DEBUG("restoring mode, cpsr = 0x%08"PRIx32
, cpsr
);
554 LOG_DEBUG("setting mode 0x%x", mode
);
558 switch (cpsr
& 0x1f) {
571 * TODO: handle ARM_MODE_HYP
581 target_el
= (cpsr
>> 2) & 3;
584 if (target_el
> SYSTEM_CUREL_EL3
) {
585 LOG_ERROR("%s: Invalid target exception level %i", __func__
, target_el
);
589 LOG_DEBUG("target_el = %i, last_el = %i", target_el
, dpm
->last_el
);
590 if (dpm
->last_el
== target_el
)
591 return ERROR_OK
; /* nothing to do */
593 if (target_el
> dpm
->last_el
) {
594 retval
= dpm
->instr_execute(dpm
,
595 armv8_opcode(armv8
, ARMV8_OPC_DCPS
) | target_el
);
597 /* DCPS clobbers registers just like an exception taken */
598 armv8_dpm_handle_exception(dpm
, false);
600 core_state
= armv8_dpm_get_core_state(dpm
);
601 if (core_state
!= ARM_STATE_AARCH64
) {
602 /* cannot do DRPS/ERET when already in EL0 */
603 if (dpm
->last_el
!= 0) {
604 /* load SPSR with the desired mode and execute DRPS */
605 LOG_DEBUG("SPSR = 0x%08"PRIx32
, cpsr
);
606 retval
= dpm
->instr_write_data_r0(dpm
,
607 ARMV8_MSR_GP_XPSR_T1(1, 0, 15), cpsr
);
608 if (retval
== ERROR_OK
)
609 retval
= dpm
->instr_execute(dpm
, armv8_opcode(armv8
, ARMV8_OPC_DRPS
));
613 * need to execute multiple DRPS instructions until target_el
616 while (retval
== ERROR_OK
&& dpm
->last_el
!= target_el
) {
617 unsigned int cur_el
= dpm
->last_el
;
618 retval
= dpm
->instr_execute(dpm
, armv8_opcode(armv8
, ARMV8_OPC_DRPS
));
619 if (cur_el
== dpm
->last_el
) {
620 LOG_INFO("Cannot reach EL %i, SPSR corrupted?", target_el
);
626 /* On executing DRPS, DSPSR and DLR become UNKNOWN, mark them as dirty */
627 dpm
->arm
->cpsr
->dirty
= true;
628 dpm
->arm
->pc
->dirty
= true;
631 * re-evaluate the core state, we might be in Aarch32 state now
632 * we rely on dpm->dscr being up-to-date
634 core_state
= armv8_dpm_get_core_state(dpm
);
635 armv8_select_opcodes(armv8
, core_state
== ARM_STATE_AARCH64
);
636 armv8_select_reg_access(armv8
, core_state
== ARM_STATE_AARCH64
);
643 * Common register read, relies on armv8_select_reg_access() having been called.
645 static int dpmv8_read_reg(struct arm_dpm
*dpm
, struct reg
*r
, unsigned regnum
)
647 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
648 int retval
= ERROR_FAIL
;
652 retval
= armv8
->read_reg_u64(armv8
, regnum
, &value_64
);
654 if (retval
== ERROR_OK
) {
657 buf_set_u64(r
->value
, 0, r
->size
, value_64
);
659 LOG_DEBUG("READ: %s, %16.8llx", r
->name
, (unsigned long long) value_64
);
661 LOG_DEBUG("READ: %s, %8.8x", r
->name
, (unsigned int) value_64
);
663 } else if (r
->size
<= 128) {
664 uint64_t lvalue
= 0, hvalue
= 0;
665 retval
= armv8
->read_reg_u128(armv8
, regnum
, &lvalue
, &hvalue
);
667 if (retval
== ERROR_OK
) {
671 buf_set_u64(r
->value
, 0, 64, lvalue
);
672 buf_set_u64(r
->value
+ 8, 0, r
->size
- 64, hvalue
);
674 LOG_DEBUG("READ: %s, lvalue=%16.8llx", r
->name
, (unsigned long long) lvalue
);
675 LOG_DEBUG("READ: %s, hvalue=%16.8llx", r
->name
, (unsigned long long) hvalue
);
679 if (retval
!= ERROR_OK
)
680 LOG_ERROR("Failed to read %s register", r
->name
);
686 * Common register write, relies on armv8_select_reg_access() having been called.
688 static int dpmv8_write_reg(struct arm_dpm
*dpm
, struct reg
*r
, unsigned regnum
)
690 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
691 int retval
= ERROR_FAIL
;
696 value_64
= buf_get_u64(r
->value
, 0, r
->size
);
697 retval
= armv8
->write_reg_u64(armv8
, regnum
, value_64
);
699 if (retval
== ERROR_OK
) {
702 LOG_DEBUG("WRITE: %s, %16.8llx", r
->name
, (unsigned long long)value_64
);
704 LOG_DEBUG("WRITE: %s, %8.8x", r
->name
, (unsigned int)value_64
);
706 } else if (r
->size
<= 128) {
707 uint64_t lvalue
, hvalue
;
709 lvalue
= buf_get_u64(r
->value
, 0, 64);
710 hvalue
= buf_get_u64(r
->value
+ 8, 0, r
->size
- 64);
711 retval
= armv8
->write_reg_u128(armv8
, regnum
, lvalue
, hvalue
);
713 if (retval
== ERROR_OK
) {
716 LOG_DEBUG("WRITE: %s, lvalue=%16.8llx", r
->name
, (unsigned long long) lvalue
);
717 LOG_DEBUG("WRITE: %s, hvalue=%16.8llx", r
->name
, (unsigned long long) hvalue
);
721 if (retval
!= ERROR_OK
)
722 LOG_ERROR("Failed to write %s register", r
->name
);
728 * Read basic registers of the current context: R0 to R15, and CPSR;
729 * sets the core mode (such as USR or IRQ) and state (such as ARM or Thumb).
730 * In normal operation this is called on entry to halting debug state,
731 * possibly after some other operations supporting restore of debug state
732 * or making sure the CPU is fully idle (drain write buffer, etc).
734 int armv8_dpm_read_current_registers(struct arm_dpm
*dpm
)
736 struct arm
*arm
= dpm
->arm
;
737 struct armv8_common
*armv8
= (struct armv8_common
*)arm
->arch_info
;
738 struct reg_cache
*cache
;
743 retval
= dpm
->prepare(dpm
);
744 if (retval
!= ERROR_OK
)
747 cache
= arm
->core_cache
;
749 /* read R0 first (it's used for scratch), then CPSR */
750 r
= cache
->reg_list
+ ARMV8_R0
;
752 retval
= dpmv8_read_reg(dpm
, r
, ARMV8_R0
);
753 if (retval
!= ERROR_OK
)
758 /* read R1, too, it will be clobbered during memory access */
759 r
= cache
->reg_list
+ ARMV8_R1
;
761 retval
= dpmv8_read_reg(dpm
, r
, ARMV8_R1
);
762 if (retval
!= ERROR_OK
)
766 /* read cpsr to r0 and get it back */
767 retval
= dpm
->instr_read_data_r0(dpm
,
768 armv8_opcode(armv8
, READ_REG_DSPSR
), &cpsr
);
769 if (retval
!= ERROR_OK
)
772 /* update core mode and state */
773 armv8_set_cpsr(arm
, cpsr
);
775 for (unsigned int i
= ARMV8_PC
; i
< cache
->num_regs
; i
++) {
776 struct arm_reg
*arm_reg
;
778 r
= armv8_reg_current(arm
, i
);
779 if (!r
->exist
|| r
->valid
)
782 /* Skip reading FP-SIMD registers */
783 if (r
->number
>= ARMV8_V0
&& r
->number
<= ARMV8_FPCR
)
787 * Only read registers that are available from the
788 * current EL (or core mode).
790 arm_reg
= r
->arch_info
;
791 if (arm_reg
->mode
!= ARM_MODE_ANY
&&
792 dpm
->last_el
!= armv8_curel_from_core_mode(arm_reg
->mode
))
795 /* Special case: ARM_MODE_SYS has no SPSR at EL1 */
796 if (r
->number
== ARMV8_SPSR_EL1
&& arm
->core_mode
== ARM_MODE_SYS
)
799 retval
= dpmv8_read_reg(dpm
, r
, i
);
800 if (retval
!= ERROR_OK
)
810 /* Avoid needless I/O ... leave breakpoints and watchpoints alone
811 * unless they're removed, or need updating because of single-stepping
812 * or running debugger code.
814 static int dpmv8_maybe_update_bpwp(struct arm_dpm
*dpm
, bool bpwp
,
815 struct dpm_bpwp
*xp
, bool *set_p
)
817 int retval
= ERROR_OK
;
824 /* removed or startup; we must disable it */
829 /* disabled, but we must set it */
830 xp
->dirty
= disable
= false;
835 /* set, but we must temporarily disable it */
836 xp
->dirty
= disable
= true;
841 retval
= dpm
->bpwp_disable(dpm
, xp
->number
);
843 retval
= dpm
->bpwp_enable(dpm
, xp
->number
,
844 xp
->address
, xp
->control
);
846 if (retval
!= ERROR_OK
)
847 LOG_ERROR("%s: can't %s HW %spoint %d",
848 disable
? "disable" : "enable",
849 target_name(dpm
->arm
->target
),
850 (xp
->number
< 16) ? "break" : "watch",
856 static int dpmv8_add_breakpoint(struct target
*target
, struct breakpoint
*bp
);
859 * Writes all modified core registers for all processor modes. In normal
860 * operation this is called on exit from halting debug state.
862 * @param dpm: represents the processor
863 * @param bpwp: true ensures breakpoints and watchpoints are set,
864 * false ensures they are cleared
866 int armv8_dpm_write_dirty_registers(struct arm_dpm
*dpm
, bool bpwp
)
868 struct arm
*arm
= dpm
->arm
;
869 struct reg_cache
*cache
= arm
->core_cache
;
872 retval
= dpm
->prepare(dpm
);
873 if (retval
!= ERROR_OK
)
876 /* If we're managing hardware breakpoints for this core, enable
877 * or disable them as requested.
879 * REVISIT We don't yet manage them for ANY cores. Eventually
880 * we should be able to assume we handle them; but until then,
881 * cope with the hand-crafted breakpoint code.
883 if (arm
->target
->type
->add_breakpoint
== dpmv8_add_breakpoint
) {
884 for (unsigned i
= 0; i
< dpm
->nbp
; i
++) {
885 struct dpm_bp
*dbp
= dpm
->dbp
+ i
;
886 struct breakpoint
*bp
= dbp
->bp
;
888 retval
= dpmv8_maybe_update_bpwp(dpm
, bpwp
, &dbp
->bpwp
,
889 bp
? &bp
->is_set
: NULL
);
890 if (retval
!= ERROR_OK
)
895 /* enable/disable watchpoints */
896 for (unsigned i
= 0; i
< dpm
->nwp
; i
++) {
897 struct dpm_wp
*dwp
= dpm
->dwp
+ i
;
898 struct watchpoint
*wp
= dwp
->wp
;
900 retval
= dpmv8_maybe_update_bpwp(dpm
, bpwp
, &dwp
->bpwp
,
901 wp
? &wp
->is_set
: NULL
);
902 if (retval
!= ERROR_OK
)
906 /* NOTE: writes to breakpoint and watchpoint registers might
907 * be queued, and need (efficient/batched) flushing later.
910 /* Restore original core mode and state */
911 retval
= armv8_dpm_modeswitch(dpm
, ARM_MODE_ANY
);
912 if (retval
!= ERROR_OK
)
915 /* check everything except our scratch register R0 */
916 for (unsigned i
= 1; i
< cache
->num_regs
; i
++) {
919 /* skip non-existent */
920 if (!cache
->reg_list
[i
].exist
)
922 /* skip PC and CPSR */
923 if (i
== ARMV8_PC
|| i
== ARMV8_XPSR
)
926 if (!cache
->reg_list
[i
].valid
)
929 if (!cache
->reg_list
[i
].dirty
)
932 /* skip all registers not on the current EL */
933 r
= cache
->reg_list
[i
].arch_info
;
934 if (r
->mode
!= ARM_MODE_ANY
&&
935 dpm
->last_el
!= armv8_curel_from_core_mode(r
->mode
))
938 retval
= dpmv8_write_reg(dpm
, &cache
->reg_list
[i
], i
);
939 if (retval
!= ERROR_OK
)
943 /* flush CPSR and PC */
944 if (retval
== ERROR_OK
)
945 retval
= dpmv8_write_reg(dpm
, &cache
->reg_list
[ARMV8_XPSR
], ARMV8_XPSR
);
946 if (retval
== ERROR_OK
)
947 retval
= dpmv8_write_reg(dpm
, &cache
->reg_list
[ARMV8_PC
], ARMV8_PC
);
948 /* flush R0 -- it's *very* dirty by now */
949 if (retval
== ERROR_OK
)
950 retval
= dpmv8_write_reg(dpm
, &cache
->reg_list
[0], 0);
951 if (retval
== ERROR_OK
)
952 dpm
->instr_cpsr_sync(dpm
);
959 * Standard ARM register accessors ... there are three methods
960 * in "struct arm", to support individual read/write and bulk read
964 static int armv8_dpm_read_core_reg(struct target
*target
, struct reg
*r
,
965 int regnum
, enum arm_mode mode
)
967 struct arm
*arm
= target_to_arm(target
);
968 struct arm_dpm
*dpm
= target_to_arm(target
)->dpm
;
970 int max
= arm
->core_cache
->num_regs
;
972 if (regnum
< 0 || regnum
>= max
)
973 return ERROR_COMMAND_SYNTAX_ERROR
;
976 * REVISIT what happens if we try to read SPSR in a core mode
977 * which has no such register?
979 retval
= dpm
->prepare(dpm
);
980 if (retval
!= ERROR_OK
)
983 retval
= dpmv8_read_reg(dpm
, r
, regnum
);
984 if (retval
!= ERROR_OK
)
988 /* (void) */ dpm
->finish(dpm
);
992 static int armv8_dpm_write_core_reg(struct target
*target
, struct reg
*r
,
993 int regnum
, enum arm_mode mode
, uint8_t *value
)
995 struct arm
*arm
= target_to_arm(target
);
996 struct arm_dpm
*dpm
= target_to_arm(target
)->dpm
;
998 int max
= arm
->core_cache
->num_regs
;
1000 if (regnum
< 0 || regnum
> max
)
1001 return ERROR_COMMAND_SYNTAX_ERROR
;
1003 /* REVISIT what happens if we try to write SPSR in a core mode
1004 * which has no such register?
1007 retval
= dpm
->prepare(dpm
);
1008 if (retval
!= ERROR_OK
)
1011 retval
= dpmv8_write_reg(dpm
, r
, regnum
);
1013 /* always clean up, regardless of error */
1019 static int armv8_dpm_full_context(struct target
*target
)
1021 struct arm
*arm
= target_to_arm(target
);
1022 struct arm_dpm
*dpm
= arm
->dpm
;
1023 struct reg_cache
*cache
= arm
->core_cache
;
1027 retval
= dpm
->prepare(dpm
);
1028 if (retval
!= ERROR_OK
)
1032 enum arm_mode mode
= ARM_MODE_ANY
;
1036 /* We "know" arm_dpm_read_current_registers() was called so
1037 * the unmapped registers (R0..R7, PC, AND CPSR) and some
1038 * view of R8..R14 are current. We also "know" oddities of
1039 * register mapping: special cases for R8..R12 and SPSR.
1041 * Pick some mode with unread registers and read them all.
1042 * Repeat until done.
1044 for (unsigned i
= 0; i
< cache
->num_regs
; i
++) {
1047 if (!cache
->reg_list
[i
].exist
|| cache
->reg_list
[i
].valid
)
1049 r
= cache
->reg_list
[i
].arch_info
;
1051 /* may need to pick a mode and set CPSR */
1056 /* For regular (ARM_MODE_ANY) R8..R12
1057 * in case we've entered debug state
1058 * in FIQ mode we need to patch mode.
1060 if (mode
!= ARM_MODE_ANY
)
1061 retval
= armv8_dpm_modeswitch(dpm
, mode
);
1063 retval
= armv8_dpm_modeswitch(dpm
, ARM_MODE_USR
);
1065 if (retval
!= ERROR_OK
)
1068 if (r
->mode
!= mode
)
1071 /* CPSR was read, so "R16" must mean SPSR */
1072 retval
= dpmv8_read_reg(dpm
,
1073 &cache
->reg_list
[i
],
1074 (r
->num
== 16) ? 17 : r
->num
);
1075 if (retval
!= ERROR_OK
)
1081 retval
= armv8_dpm_modeswitch(dpm
, ARM_MODE_ANY
);
1082 /* (void) */ dpm
->finish(dpm
);
1088 /*----------------------------------------------------------------------*/
1091 * Breakpoint and Watchpoint support.
1093 * Hardware {break,watch}points are usually left active, to minimize
1094 * debug entry/exit costs. When they are set or cleared, it's done in
1095 * batches. Also, DPM-conformant hardware can update debug registers
1096 * regardless of whether the CPU is running or halted ... though that
1097 * fact isn't currently leveraged.
1100 static int dpmv8_bpwp_setup(struct arm_dpm
*dpm
, struct dpm_bpwp
*xp
,
1101 uint32_t addr
, uint32_t length
)
1105 control
= (1 << 0) /* enable */
1106 | (3 << 1); /* both user and privileged access */
1108 /* Match 1, 2, or all 4 byte addresses in this word.
1110 * FIXME: v7 hardware allows lengths up to 2 GB for BP and WP.
1111 * Support larger length, when addr is suitably aligned. In
1112 * particular, allow watchpoints on 8 byte "double" values.
1114 * REVISIT allow watchpoints on unaligned 2-bit values; and on
1115 * v7 hardware, unaligned 4-byte ones too.
1119 control
|= (1 << (addr
& 3)) << 5;
1122 /* require 2-byte alignment */
1124 control
|= (3 << (addr
& 2)) << 5;
1129 /* require 4-byte alignment */
1131 control
|= 0xf << 5;
1136 LOG_ERROR("unsupported {break,watch}point length/alignment");
1137 return ERROR_COMMAND_SYNTAX_ERROR
;
1140 /* other shared control bits:
1141 * bits 15:14 == 0 ... both secure and nonsecure states (v6.1+ only)
1142 * bit 20 == 0 ... not linked to a context ID
1143 * bit 28:24 == 0 ... not ignoring N LSBs (v7 only)
1146 xp
->address
= addr
& ~3;
1147 xp
->control
= control
;
1150 LOG_DEBUG("BPWP: addr %8.8" PRIx32
", control %" PRIx32
", number %d",
1151 xp
->address
, control
, xp
->number
);
1153 /* hardware is updated in write_dirty_registers() */
1157 static int dpmv8_add_breakpoint(struct target
*target
, struct breakpoint
*bp
)
1159 struct arm
*arm
= target_to_arm(target
);
1160 struct arm_dpm
*dpm
= arm
->dpm
;
1161 int retval
= ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
1164 return ERROR_COMMAND_SYNTAX_ERROR
;
1165 if (!dpm
->bpwp_enable
)
1168 /* FIXME we need a generic solution for software breakpoints. */
1169 if (bp
->type
== BKPT_SOFT
)
1170 LOG_DEBUG("using HW bkpt, not SW...");
1172 for (unsigned i
= 0; i
< dpm
->nbp
; i
++) {
1173 if (!dpm
->dbp
[i
].bp
) {
1174 retval
= dpmv8_bpwp_setup(dpm
, &dpm
->dbp
[i
].bpwp
,
1175 bp
->address
, bp
->length
);
1176 if (retval
== ERROR_OK
)
1177 dpm
->dbp
[i
].bp
= bp
;
1185 static int dpmv8_remove_breakpoint(struct target
*target
, struct breakpoint
*bp
)
1187 struct arm
*arm
= target_to_arm(target
);
1188 struct arm_dpm
*dpm
= arm
->dpm
;
1189 int retval
= ERROR_COMMAND_SYNTAX_ERROR
;
1191 for (unsigned i
= 0; i
< dpm
->nbp
; i
++) {
1192 if (dpm
->dbp
[i
].bp
== bp
) {
1193 dpm
->dbp
[i
].bp
= NULL
;
1194 dpm
->dbp
[i
].bpwp
.dirty
= true;
1196 /* hardware is updated in write_dirty_registers() */
1205 static int dpmv8_watchpoint_setup(struct arm_dpm
*dpm
, unsigned index_t
,
1206 struct watchpoint
*wp
)
1209 struct dpm_wp
*dwp
= dpm
->dwp
+ index_t
;
1212 /* this hardware doesn't support data value matching or masking */
1213 if (wp
->mask
!= WATCHPOINT_IGNORE_DATA_VALUE_MASK
) {
1214 LOG_DEBUG("watchpoint values and masking not supported");
1215 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
1218 retval
= dpmv8_bpwp_setup(dpm
, &dwp
->bpwp
, wp
->address
, wp
->length
);
1219 if (retval
!= ERROR_OK
)
1222 control
= dwp
->bpwp
.control
;
1234 dwp
->bpwp
.control
= control
;
1236 dpm
->dwp
[index_t
].wp
= wp
;
1241 static int dpmv8_add_watchpoint(struct target
*target
, struct watchpoint
*wp
)
1243 struct arm
*arm
= target_to_arm(target
);
1244 struct arm_dpm
*dpm
= arm
->dpm
;
1245 int retval
= ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
1247 if (dpm
->bpwp_enable
) {
1248 for (unsigned i
= 0; i
< dpm
->nwp
; i
++) {
1249 if (!dpm
->dwp
[i
].wp
) {
1250 retval
= dpmv8_watchpoint_setup(dpm
, i
, wp
);
1259 static int dpmv8_remove_watchpoint(struct target
*target
, struct watchpoint
*wp
)
1261 struct arm
*arm
= target_to_arm(target
);
1262 struct arm_dpm
*dpm
= arm
->dpm
;
1263 int retval
= ERROR_COMMAND_SYNTAX_ERROR
;
1265 for (unsigned i
= 0; i
< dpm
->nwp
; i
++) {
1266 if (dpm
->dwp
[i
].wp
== wp
) {
1267 dpm
->dwp
[i
].wp
= NULL
;
1268 dpm
->dwp
[i
].bpwp
.dirty
= true;
1270 /* hardware is updated in write_dirty_registers() */
1280 * Handle exceptions taken in debug state. This happens mostly for memory
1281 * accesses that violated a MMU policy. Taking an exception while in debug
1282 * state clobbers certain state registers on the target exception level.
1283 * Just mark those registers dirty so that they get restored on resume.
1284 * This works both for Aarch32 and Aarch64 states.
1286 * This function must not perform any actions that trigger another exception
1287 * or a recursion will happen.
1289 void armv8_dpm_handle_exception(struct arm_dpm
*dpm
, bool do_restore
)
1291 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
1292 struct reg_cache
*cache
= dpm
->arm
->core_cache
;
1293 enum arm_state core_state
;
1298 static const int clobbered_regs_by_el
[3][5] = {
1299 { ARMV8_PC
, ARMV8_XPSR
, ARMV8_ELR_EL1
, ARMV8_ESR_EL1
, ARMV8_SPSR_EL1
},
1300 { ARMV8_PC
, ARMV8_XPSR
, ARMV8_ELR_EL2
, ARMV8_ESR_EL2
, ARMV8_SPSR_EL2
},
1301 { ARMV8_PC
, ARMV8_XPSR
, ARMV8_ELR_EL3
, ARMV8_ESR_EL3
, ARMV8_SPSR_EL3
},
1304 el
= (dpm
->dscr
>> 8) & 3;
1306 /* safety check, must not happen since EL0 cannot be a target for an exception */
1307 if (el
< SYSTEM_CUREL_EL1
|| el
> SYSTEM_CUREL_EL3
) {
1308 LOG_ERROR("%s: EL %i is invalid, DSCR corrupted?", __func__
, el
);
1312 /* Clear sticky error */
1313 mem_ap_write_u32(armv8
->debug_ap
,
1314 armv8
->debug_base
+ CPUV8_DBG_DRCR
, DRCR_CSE
);
1316 armv8
->read_reg_u64(armv8
, ARMV8_XPSR
, &dlr
);
1318 armv8
->read_reg_u64(armv8
, ARMV8_PC
, &dlr
);
1320 LOG_DEBUG("Exception taken to EL %i, DLR=0x%016"PRIx64
" DSPSR=0x%08"PRIx32
,
1323 /* mark all clobbered registers as dirty */
1324 for (int i
= 0; i
< 5; i
++)
1325 cache
->reg_list
[clobbered_regs_by_el
[el
-1][i
]].dirty
= true;
1328 * re-evaluate the core state, we might be in Aarch64 state now
1329 * we rely on dpm->dscr being up-to-date
1331 core_state
= armv8_dpm_get_core_state(dpm
);
1332 armv8_select_opcodes(armv8
, core_state
== ARM_STATE_AARCH64
);
1333 armv8_select_reg_access(armv8
, core_state
== ARM_STATE_AARCH64
);
1336 armv8_dpm_modeswitch(dpm
, ARM_MODE_ANY
);
1339 /*----------------------------------------------------------------------*/
1342 * Other debug and support utilities
1345 void armv8_dpm_report_dscr(struct arm_dpm
*dpm
, uint32_t dscr
)
1347 struct target
*target
= dpm
->arm
->target
;
1350 dpm
->last_el
= (dscr
>> 8) & 3;
1352 /* Examine debug reason */
1353 switch (DSCR_ENTRY(dscr
)) {
1354 /* FALL THROUGH -- assume a v6 core in abort mode */
1355 case DSCRV8_ENTRY_EXT_DEBUG
: /* EDBGRQ */
1356 target
->debug_reason
= DBG_REASON_DBGRQ
;
1358 case DSCRV8_ENTRY_HALT_STEP_EXECLU
: /* HALT step */
1359 case DSCRV8_ENTRY_HALT_STEP_NORMAL
: /* Halt step*/
1360 case DSCRV8_ENTRY_HALT_STEP
:
1361 target
->debug_reason
= DBG_REASON_SINGLESTEP
;
1363 case DSCRV8_ENTRY_HLT
: /* HLT instruction (software breakpoint) */
1364 case DSCRV8_ENTRY_BKPT
: /* SW BKPT (?) */
1365 case DSCRV8_ENTRY_RESET_CATCH
: /* Reset catch */
1366 case DSCRV8_ENTRY_OS_UNLOCK
: /*OS unlock catch*/
1367 case DSCRV8_ENTRY_SW_ACCESS_DBG
: /*SW access dbg register*/
1368 target
->debug_reason
= DBG_REASON_BREAKPOINT
;
1370 case DSCRV8_ENTRY_WATCHPOINT
: /* asynch watchpoint */
1371 target
->debug_reason
= DBG_REASON_WATCHPOINT
;
1373 case DSCRV8_ENTRY_EXCEPTION_CATCH
: /*exception catch*/
1374 target
->debug_reason
= DBG_REASON_EXC_CATCH
;
1377 target
->debug_reason
= DBG_REASON_UNDEFINED
;
1383 /*----------------------------------------------------------------------*/
1386 * Setup and management support.
1390 * Hooks up this DPM to its associated target; call only once.
1391 * Initially this only covers the register cache.
1393 * Oh, and watchpoints. Yeah.
1395 int armv8_dpm_setup(struct arm_dpm
*dpm
)
1397 struct arm
*arm
= dpm
->arm
;
1398 struct target
*target
= arm
->target
;
1399 struct reg_cache
*cache
;
1402 /* register access setup */
1403 arm
->full_context
= armv8_dpm_full_context
;
1404 arm
->read_core_reg
= armv8_dpm_read_core_reg
;
1405 arm
->write_core_reg
= armv8_dpm_write_core_reg
;
1407 if (!arm
->core_cache
) {
1408 cache
= armv8_build_reg_cache(target
);
1413 /* coprocessor access setup */
1414 arm
->mrc
= dpmv8_mrc
;
1415 arm
->mcr
= dpmv8_mcr
;
1417 dpm
->prepare
= dpmv8_dpm_prepare
;
1418 dpm
->finish
= dpmv8_dpm_finish
;
1420 dpm
->instr_execute
= dpmv8_instr_execute
;
1421 dpm
->instr_write_data_dcc
= dpmv8_instr_write_data_dcc
;
1422 dpm
->instr_write_data_dcc_64
= dpmv8_instr_write_data_dcc_64
;
1423 dpm
->instr_write_data_r0
= dpmv8_instr_write_data_r0
;
1424 dpm
->instr_write_data_r0_64
= dpmv8_instr_write_data_r0_64
;
1425 dpm
->instr_cpsr_sync
= dpmv8_instr_cpsr_sync
;
1427 dpm
->instr_read_data_dcc
= dpmv8_instr_read_data_dcc
;
1428 dpm
->instr_read_data_dcc_64
= dpmv8_instr_read_data_dcc_64
;
1429 dpm
->instr_read_data_r0
= dpmv8_instr_read_data_r0
;
1430 dpm
->instr_read_data_r0_64
= dpmv8_instr_read_data_r0_64
;
1432 dpm
->arm_reg_current
= armv8_reg_current
;
1434 /* dpm->bpwp_enable = dpmv8_bpwp_enable; */
1435 dpm
->bpwp_disable
= dpmv8_bpwp_disable
;
1437 /* breakpoint setup -- optional until it works everywhere */
1438 if (!target
->type
->add_breakpoint
) {
1439 target
->type
->add_breakpoint
= dpmv8_add_breakpoint
;
1440 target
->type
->remove_breakpoint
= dpmv8_remove_breakpoint
;
1443 /* watchpoint setup */
1444 if (!target
->type
->add_watchpoint
) {
1445 target
->type
->add_watchpoint
= dpmv8_add_watchpoint
;
1446 target
->type
->remove_watchpoint
= dpmv8_remove_watchpoint
;
1449 /* FIXME add vector catch support */
1451 dpm
->nbp
= 1 + ((dpm
->didr
>> 12) & 0xf);
1452 dpm
->dbp
= calloc(dpm
->nbp
, sizeof(*dpm
->dbp
));
1454 dpm
->nwp
= 1 + ((dpm
->didr
>> 20) & 0xf);
1455 dpm
->dwp
= calloc(dpm
->nwp
, sizeof(*dpm
->dwp
));
1457 if (!dpm
->dbp
|| !dpm
->dwp
) {
1463 LOG_INFO("%s: hardware has %d breakpoints, %d watchpoints",
1464 target_name(target
), dpm
->nbp
, dpm
->nwp
);
1466 /* REVISIT ... and some of those breakpoints could match
1467 * execution context IDs...
1474 * Reinitializes DPM state at the beginning of a new debug session
1475 * or after a reset which may have affected the debug module.
1477 int armv8_dpm_initialize(struct arm_dpm
*dpm
)
1479 /* Disable all breakpoints and watchpoints at startup. */
1480 if (dpm
->bpwp_disable
) {
1483 for (i
= 0; i
< dpm
->nbp
; i
++) {
1484 dpm
->dbp
[i
].bpwp
.number
= i
;
1485 (void) dpm
->bpwp_disable(dpm
, i
);
1487 for (i
= 0; i
< dpm
->nwp
; i
++) {
1488 dpm
->dwp
[i
].bpwp
.number
= 16 + i
;
1489 (void) dpm
->bpwp_disable(dpm
, 16 + i
);
1492 LOG_WARNING("%s: can't disable breakpoints and watchpoints",
1493 target_name(dpm
->arm
->target
));
Linking to existing account procedure
If you already have an account and want to add another login method
you
MUST first sign in with your existing account and
then change URL to read
https://review.openocd.org/login/?link
to get to this page again but this time it'll work for linking. Thank you.
SSH host keys fingerprints
1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=.. |
|+o.. . |
|*.o . . |
|+B . . . |
|Bo. = o S |
|Oo.+ + = |
|oB=.* = . o |
| =+=.+ + E |
|. .=o . o |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)