1 /***************************************************************************
2 * Copyright (C) 2011 by Mathias Kuester *
3 * Mathias Kuester <kesmtp@freenet.de> *
5 * Copyright (C) 2011 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
28 #include "jtag/jtag.h"
29 #include "jtag/hla/hla_transport.h"
30 #include "jtag/hla/hla_interface.h"
31 #include "jtag/hla/hla_layout.h"
33 #include "algorithm.h"
35 #include "breakpoints.h"
36 #include "target_type.h"
39 #include "arm_semihosting.h"
41 #define ARMV7M_SCS_DCRSR 0xe000edf4
42 #define ARMV7M_SCS_DCRDR 0xe000edf8
44 static inline struct hl_interface_s
*target_to_adapter(struct target
*target
)
46 return target
->tap
->priv
;
49 static int adapter_load_core_reg_u32(struct target
*target
,
50 enum armv7m_regtype type
,
51 uint32_t num
, uint32_t *value
)
54 struct hl_interface_s
*adapter
= target_to_adapter(target
);
56 LOG_DEBUG("%s", __func__
);
58 /* NOTE: we "know" here that the register identifiers used
59 * in the v7m header match the Cortex-M3 Debug Core Register
60 * Selector values for R0..R15, xPSR, MSP, and PSP.
64 /* read a normal core register */
65 retval
= adapter
->layout
->api
->read_reg(adapter
->fd
, num
, value
);
67 if (retval
!= ERROR_OK
) {
68 LOG_ERROR("JTAG failure %i", retval
);
69 return ERROR_JTAG_DEVICE_ERROR
;
71 LOG_DEBUG("load from core reg %i value 0x%" PRIx32
"", (int)num
, *value
);
80 /* Floating-point Status and Registers */
81 retval
= target_write_u32(target
, ARMV7M_SCS_DCRSR
, 33);
82 if (retval
!= ERROR_OK
)
84 retval
= target_read_u32(target
, ARMV7M_SCS_DCRDR
, value
);
85 if (retval
!= ERROR_OK
)
87 LOG_DEBUG("load from core reg %i value 0x%" PRIx32
"", (int)num
, *value
);
90 case ARMV7M_S0
... ARMV7M_S31
:
91 /* Floating-point Status and Registers */
92 retval
= target_write_u32(target
, ARMV7M_SCS_DCRSR
, num
-ARMV7M_S0
+64);
93 if (retval
!= ERROR_OK
)
95 retval
= target_read_u32(target
, ARMV7M_SCS_DCRDR
, value
);
96 if (retval
!= ERROR_OK
)
98 LOG_DEBUG("load from core reg %i value 0x%" PRIx32
"", (int)num
, *value
);
101 case ARMV7M_D0
... ARMV7M_D15
:
107 case ARMV7M_FAULTMASK
:
109 /* Cortex-M3 packages these four registers as bitfields
110 * in one Debug Core register. So say r0 and r2 docs;
111 * it was removed from r1 docs, but still works.
113 retval
= adapter
->layout
->api
->read_reg(adapter
->fd
, 20, value
);
114 if (retval
!= ERROR_OK
)
119 *value
= buf_get_u32((uint8_t *) value
, 0, 1);
123 *value
= buf_get_u32((uint8_t *) value
, 8, 8);
126 case ARMV7M_FAULTMASK
:
127 *value
= buf_get_u32((uint8_t *) value
, 16, 1);
131 *value
= buf_get_u32((uint8_t *) value
, 24, 2);
135 LOG_DEBUG("load from special reg %i value 0x%" PRIx32
"",
140 return ERROR_COMMAND_SYNTAX_ERROR
;
146 static int adapter_store_core_reg_u32(struct target
*target
,
147 enum armv7m_regtype type
,
148 uint32_t num
, uint32_t value
)
152 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
153 struct hl_interface_s
*adapter
= target_to_adapter(target
);
155 LOG_DEBUG("%s", __func__
);
157 #ifdef ARMV7_GDB_HACKS
158 /* If the LR register is being modified, make sure it will put us
159 * in "thumb" mode, or an INVSTATE exception will occur. This is a
160 * hack to deal with the fact that gdb will sometimes "forge"
161 * return addresses, and doesn't set the LSB correctly (i.e., when
162 * printing expressions containing function calls, it sets LR = 0.)
163 * Valid exception return codes have bit 0 set too.
165 if (num
== ARMV7M_R14
)
169 /* NOTE: we "know" here that the register identifiers used
170 * in the v7m header match the Cortex-M3 Debug Core Register
171 * Selector values for R0..R15, xPSR, MSP, and PSP.
175 retval
= adapter
->layout
->api
->write_reg(adapter
->fd
, num
, value
);
177 if (retval
!= ERROR_OK
) {
180 LOG_ERROR("JTAG failure");
181 r
= armv7m
->core_cache
->reg_list
+ num
;
183 return ERROR_JTAG_DEVICE_ERROR
;
185 LOG_DEBUG("write core reg %i value 0x%" PRIx32
"", (int)num
, value
);
193 /* Floating-point Status and Registers */
194 retval
= target_write_u32(target
, ARMV7M_SCS_DCRDR
, value
);
195 if (retval
!= ERROR_OK
)
197 retval
= target_write_u32(target
, ARMV7M_SCS_DCRSR
, 33 | (1<<16));
198 if (retval
!= ERROR_OK
)
200 LOG_DEBUG("write core reg %i value 0x%" PRIx32
"", (int)num
, value
);
203 case ARMV7M_S0
... ARMV7M_S31
:
204 /* Floating-point Status and Registers */
205 retval
= target_write_u32(target
, ARMV7M_SCS_DCRDR
, value
);
206 if (retval
!= ERROR_OK
)
208 retval
= target_write_u32(target
, ARMV7M_SCS_DCRSR
, (num
-ARMV7M_S0
+64) | (1<<16));
209 if (retval
!= ERROR_OK
)
211 LOG_DEBUG("write core reg %i value 0x%" PRIx32
"", (int)num
, value
);
214 case ARMV7M_D0
... ARMV7M_D15
:
219 case ARMV7M_FAULTMASK
:
221 /* Cortex-M3 packages these four registers as bitfields
222 * in one Debug Core register. So say r0 and r2 docs;
223 * it was removed from r1 docs, but still works.
226 adapter
->layout
->api
->read_reg(adapter
->fd
, 20, ®
);
230 buf_set_u32((uint8_t *) ®
, 0, 1, value
);
234 buf_set_u32((uint8_t *) ®
, 8, 8, value
);
237 case ARMV7M_FAULTMASK
:
238 buf_set_u32((uint8_t *) ®
, 16, 1, value
);
242 buf_set_u32((uint8_t *) ®
, 24, 2, value
);
246 adapter
->layout
->api
->write_reg(adapter
->fd
, 20, reg
);
248 LOG_DEBUG("write special reg %i value 0x%" PRIx32
" ", (int)num
, value
);
252 return ERROR_COMMAND_SYNTAX_ERROR
;
258 static int adapter_examine_debug_reason(struct target
*target
)
260 if ((target
->debug_reason
!= DBG_REASON_DBGRQ
)
261 && (target
->debug_reason
!= DBG_REASON_SINGLESTEP
)) {
262 target
->debug_reason
= DBG_REASON_BREAKPOINT
;
268 static int adapter_init_arch_info(struct target
*target
,
269 struct cortex_m3_common
*cortex_m3
,
270 struct jtag_tap
*tap
)
272 struct armv7m_common
*armv7m
;
274 LOG_DEBUG("%s", __func__
);
276 armv7m
= &cortex_m3
->armv7m
;
277 armv7m_init_arch_info(target
, armv7m
);
279 armv7m
->load_core_reg_u32
= adapter_load_core_reg_u32
;
280 armv7m
->store_core_reg_u32
= adapter_store_core_reg_u32
;
282 armv7m
->examine_debug_reason
= adapter_examine_debug_reason
;
283 armv7m
->stlink
= true;
288 static int adapter_init_target(struct command_context
*cmd_ctx
,
289 struct target
*target
)
291 LOG_DEBUG("%s", __func__
);
293 armv7m_build_reg_cache(target
);
298 static int adapter_target_create(struct target
*target
,
301 LOG_DEBUG("%s", __func__
);
303 struct cortex_m3_common
*cortex_m3
= calloc(1, sizeof(struct cortex_m3_common
));
306 return ERROR_COMMAND_SYNTAX_ERROR
;
308 adapter_init_arch_info(target
, cortex_m3
, target
->tap
);
313 static int adapter_load_context(struct target
*target
)
315 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
316 int num_regs
= armv7m
->core_cache
->num_regs
;
318 for (int i
= 0; i
< num_regs
; i
++) {
319 if (!armv7m
->core_cache
->reg_list
[i
].valid
)
320 armv7m
->read_core_reg(target
, i
);
326 static int adapter_debug_entry(struct target
*target
)
328 struct hl_interface_s
*adapter
= target_to_adapter(target
);
329 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
330 struct arm
*arm
= &armv7m
->arm
;
335 retval
= armv7m
->examine_debug_reason(target
);
336 if (retval
!= ERROR_OK
)
339 adapter_load_context(target
);
341 /* make sure we clear the vector catch bit */
342 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DEMCR
, TRCENA
);
344 r
= armv7m
->core_cache
->reg_list
+ ARMV7M_xPSR
;
345 xPSR
= buf_get_u32(r
->value
, 0, 32);
347 /* Are we in an exception handler */
349 armv7m
->exception_number
= (xPSR
& 0x1FF);
351 arm
->core_mode
= ARM_MODE_HANDLER
;
352 arm
->map
= armv7m_msp_reg_map
;
354 unsigned control
= buf_get_u32(arm
->core_cache
355 ->reg_list
[ARMV7M_CONTROL
].value
, 0, 2);
357 /* is this thread privileged? */
358 arm
->core_mode
= control
& 1
359 ? ARM_MODE_USER_THREAD
362 /* which stack is it using? */
364 arm
->map
= armv7m_psp_reg_map
;
366 arm
->map
= armv7m_msp_reg_map
;
368 armv7m
->exception_number
= 0;
371 LOG_DEBUG("entered debug state in core mode: %s at PC 0x%08" PRIx32
", target->state: %s",
372 arm_mode_name(arm
->core_mode
),
373 *(uint32_t *)(arm
->pc
->value
),
374 target_state_name(target
));
379 static int adapter_poll(struct target
*target
)
381 enum target_state state
;
382 struct hl_interface_s
*adapter
= target_to_adapter(target
);
383 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
385 state
= adapter
->layout
->api
->state(adapter
->fd
);
387 if (state
== TARGET_UNKNOWN
) {
388 LOG_ERROR("jtag status contains invalid mode value - communication failure");
389 return ERROR_TARGET_FAILURE
;
392 if (target
->state
== state
)
395 if (state
== TARGET_HALTED
) {
396 target
->state
= state
;
398 int retval
= adapter_debug_entry(target
);
399 if (retval
!= ERROR_OK
)
402 if (arm_semihosting(target
, &retval
) != 0)
405 target_call_event_callbacks(target
, TARGET_EVENT_HALTED
);
406 LOG_DEBUG("halted: PC: 0x%08x", buf_get_u32(armv7m
->arm
.pc
->value
, 0, 32));
412 static int adapter_assert_reset(struct target
*target
)
415 struct hl_interface_s
*adapter
= target_to_adapter(target
);
416 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
417 bool use_srst_fallback
= true;
419 LOG_DEBUG("%s", __func__
);
421 enum reset_types jtag_reset_config
= jtag_get_reset_config();
423 bool srst_asserted
= false;
425 if (jtag_reset_config
& RESET_SRST_NO_GATING
) {
426 jtag_add_reset(0, 1);
427 res
= adapter
->layout
->api
->assert_srst(adapter
->fd
, 0);
428 srst_asserted
= true;
431 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DHCSR
, DBGKEY
|C_DEBUGEN
);
433 /* only set vector catch if halt is requested */
434 if (target
->reset_halt
)
435 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DEMCR
, TRCENA
|VC_CORERESET
);
437 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DEMCR
, TRCENA
);
439 if (jtag_reset_config
& RESET_HAS_SRST
) {
440 if (!srst_asserted
) {
441 jtag_add_reset(0, 1);
442 res
= adapter
->layout
->api
->assert_srst(adapter
->fd
, 0);
444 if (res
== ERROR_COMMAND_NOTFOUND
)
445 LOG_ERROR("Hardware srst not supported, falling back to software reset");
446 else if (res
== ERROR_OK
) {
447 /* hardware srst supported */
448 use_srst_fallback
= false;
452 if (use_srst_fallback
) {
453 /* stlink v1 api does not support hardware srst, so we use a software reset fallback */
454 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, NVIC_AIRCR
, AIRCR_VECTKEY
| AIRCR_SYSRESETREQ
);
457 res
= adapter
->layout
->api
->reset(adapter
->fd
);
462 /* registers are now invalid */
463 register_cache_invalidate(armv7m
->core_cache
);
465 if (target
->reset_halt
) {
466 target
->state
= TARGET_RESET
;
467 target
->debug_reason
= DBG_REASON_DBGRQ
;
469 target
->state
= TARGET_HALTED
;
475 static int adapter_deassert_reset(struct target
*target
)
478 struct hl_interface_s
*adapter
= target_to_adapter(target
);
480 enum reset_types jtag_reset_config
= jtag_get_reset_config();
482 LOG_DEBUG("%s", __func__
);
484 if (jtag_reset_config
& RESET_HAS_SRST
)
485 adapter
->layout
->api
->assert_srst(adapter
->fd
, 1);
487 /* virtual deassert reset, we need it for the internal
490 jtag_add_reset(0, 0);
492 if (!target
->reset_halt
) {
493 res
= target_resume(target
, 1, 0, 0, 0);
502 static int adapter_soft_reset_halt(struct target
*target
)
504 LOG_DEBUG("%s", __func__
);
508 static int adapter_halt(struct target
*target
)
511 struct hl_interface_s
*adapter
= target_to_adapter(target
);
513 LOG_DEBUG("%s", __func__
);
515 if (target
->state
== TARGET_HALTED
) {
516 LOG_DEBUG("target was already halted");
520 if (target
->state
== TARGET_UNKNOWN
)
521 LOG_WARNING("target was in unknown state when halt was requested");
523 res
= adapter
->layout
->api
->halt(adapter
->fd
);
528 target
->debug_reason
= DBG_REASON_DBGRQ
;
533 static int adapter_resume(struct target
*target
, int current
,
534 uint32_t address
, int handle_breakpoints
,
538 struct hl_interface_s
*adapter
= target_to_adapter(target
);
539 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
541 struct breakpoint
*breakpoint
= NULL
;
544 LOG_DEBUG("%s %d 0x%08x %d %d", __func__
, current
, address
,
545 handle_breakpoints
, debug_execution
);
547 if (target
->state
!= TARGET_HALTED
) {
548 LOG_WARNING("target not halted");
549 return ERROR_TARGET_NOT_HALTED
;
552 if (!debug_execution
) {
553 target_free_all_working_areas(target
);
554 cortex_m3_enable_breakpoints(target
);
555 cortex_m3_enable_watchpoints(target
);
560 buf_set_u32(pc
->value
, 0, 32, address
);
565 if (!breakpoint_find(target
, buf_get_u32(pc
->value
, 0, 32))
566 && !debug_execution
) {
567 armv7m_maybe_skip_bkpt_inst(target
, NULL
);
570 resume_pc
= buf_get_u32(pc
->value
, 0, 32);
572 /* write any user vector flags */
573 res
= target_write_u32(target
, DCB_DEMCR
, TRCENA
| armv7m
->demcr
);
577 armv7m_restore_context(target
);
579 /* registers are now invalid */
580 register_cache_invalidate(armv7m
->core_cache
);
582 /* the front-end may request us not to handle breakpoints */
583 if (handle_breakpoints
) {
584 /* Single step past breakpoint at current address */
585 breakpoint
= breakpoint_find(target
, resume_pc
);
587 LOG_DEBUG("unset breakpoint at 0x%8.8" PRIx32
" (ID: %d)",
589 breakpoint
->unique_id
);
590 cortex_m3_unset_breakpoint(target
, breakpoint
);
592 res
= adapter
->layout
->api
->step(adapter
->fd
);
597 cortex_m3_set_breakpoint(target
, breakpoint
);
601 res
= adapter
->layout
->api
->run(adapter
->fd
);
606 target
->state
= TARGET_RUNNING
;
607 target
->debug_reason
= DBG_REASON_NOTHALTED
;
609 target_call_event_callbacks(target
, TARGET_EVENT_RESUMED
);
614 static int adapter_step(struct target
*target
, int current
,
615 uint32_t address
, int handle_breakpoints
)
618 struct hl_interface_s
*adapter
= target_to_adapter(target
);
619 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
620 struct breakpoint
*breakpoint
= NULL
;
621 struct reg
*pc
= armv7m
->arm
.pc
;
622 bool bkpt_inst_found
= false;
624 LOG_DEBUG("%s", __func__
);
626 if (target
->state
!= TARGET_HALTED
) {
627 LOG_WARNING("target not halted");
628 return ERROR_TARGET_NOT_HALTED
;
632 buf_set_u32(pc
->value
, 0, 32, address
);
637 uint32_t pc_value
= buf_get_u32(pc
->value
, 0, 32);
639 /* the front-end may request us not to handle breakpoints */
640 if (handle_breakpoints
) {
641 breakpoint
= breakpoint_find(target
, pc_value
);
643 cortex_m3_unset_breakpoint(target
, breakpoint
);
646 armv7m_maybe_skip_bkpt_inst(target
, &bkpt_inst_found
);
648 target
->debug_reason
= DBG_REASON_SINGLESTEP
;
650 armv7m_restore_context(target
);
652 target_call_event_callbacks(target
, TARGET_EVENT_RESUMED
);
654 res
= adapter
->layout
->api
->step(adapter
->fd
);
659 /* registers are now invalid */
660 register_cache_invalidate(armv7m
->core_cache
);
663 cortex_m3_set_breakpoint(target
, breakpoint
);
665 adapter_debug_entry(target
);
666 target_call_event_callbacks(target
, TARGET_EVENT_HALTED
);
668 LOG_INFO("halted: PC: 0x%08x", buf_get_u32(armv7m
->arm
.pc
->value
, 0, 32));
673 static int adapter_read_memory(struct target
*target
, uint32_t address
,
674 uint32_t size
, uint32_t count
,
677 struct hl_interface_s
*adapter
= target_to_adapter(target
);
679 uint32_t buffer_threshold
= (adapter
->param
.max_buffer
/ 4);
680 uint32_t addr_increment
= 4;
683 if (!count
|| !buffer
)
684 return ERROR_COMMAND_SYNTAX_ERROR
;
686 LOG_DEBUG("%s 0x%08x %d %d", __func__
, address
, size
, count
);
688 /* prepare byte count, buffer threshold
689 * and address increment for none 32bit access
693 buffer_threshold
= (adapter
->param
.max_buffer
/ 4) / 2;
698 if (count
> buffer_threshold
)
699 c
= buffer_threshold
;
704 res
= adapter
->layout
->api
->read_mem8(adapter
->fd
,
707 res
= adapter
->layout
->api
->read_mem32(adapter
->fd
,
713 address
+= (c
* addr_increment
);
714 buffer
+= (c
* addr_increment
);
721 static int adapter_write_memory(struct target
*target
, uint32_t address
,
722 uint32_t size
, uint32_t count
,
723 const uint8_t *buffer
)
725 struct hl_interface_s
*adapter
= target_to_adapter(target
);
727 uint32_t buffer_threshold
= (adapter
->param
.max_buffer
/ 4);
728 uint32_t addr_increment
= 4;
731 if (!count
|| !buffer
)
732 return ERROR_COMMAND_SYNTAX_ERROR
;
734 LOG_DEBUG("%s 0x%08x %d %d", __func__
, address
, size
, count
);
736 /* prepare byte count, buffer threshold
737 * and address increment for none 32bit access
741 buffer_threshold
= (adapter
->param
.max_buffer
/ 4) / 2;
746 if (count
> buffer_threshold
)
747 c
= buffer_threshold
;
752 res
= adapter
->layout
->api
->write_mem8(adapter
->fd
,
755 res
= adapter
->layout
->api
->write_mem32(adapter
->fd
,
761 address
+= (c
* addr_increment
);
762 buffer
+= (c
* addr_increment
);
769 static int adapter_bulk_write_memory(struct target
*target
,
770 uint32_t address
, uint32_t count
,
771 const uint8_t *buffer
)
773 return adapter_write_memory(target
, address
, 4, count
, buffer
);
776 static const struct command_registration adapter_command_handlers
[] = {
778 .chain
= arm_command_handlers
,
780 COMMAND_REGISTRATION_DONE
783 struct target_type hla_target
= {
784 .name
= "hla_target",
785 .deprecated_name
= "stm32_stlink",
787 .init_target
= adapter_init_target
,
788 .target_create
= adapter_target_create
,
789 .examine
= cortex_m3_examine
,
790 .commands
= adapter_command_handlers
,
792 .poll
= adapter_poll
,
793 .arch_state
= armv7m_arch_state
,
795 .assert_reset
= adapter_assert_reset
,
796 .deassert_reset
= adapter_deassert_reset
,
797 .soft_reset_halt
= adapter_soft_reset_halt
,
799 .halt
= adapter_halt
,
800 .resume
= adapter_resume
,
801 .step
= adapter_step
,
803 .get_gdb_reg_list
= armv7m_get_gdb_reg_list
,
805 .read_memory
= adapter_read_memory
,
806 .write_memory
= adapter_write_memory
,
807 .bulk_write_memory
= adapter_bulk_write_memory
,
808 .checksum_memory
= armv7m_checksum_memory
,
809 .blank_check_memory
= armv7m_blank_check_memory
,
811 .run_algorithm
= armv7m_run_algorithm
,
812 .start_algorithm
= armv7m_start_algorithm
,
813 .wait_algorithm
= armv7m_wait_algorithm
,
815 .add_breakpoint
= cortex_m3_add_breakpoint
,
816 .remove_breakpoint
= cortex_m3_remove_breakpoint
,
817 .add_watchpoint
= cortex_m3_add_watchpoint
,
818 .remove_watchpoint
= cortex_m3_remove_watchpoint
,
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)