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
, 0);
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
->core_mode
= ARMV7M_MODE_HANDLER
;
350 armv7m
->exception_number
= (xPSR
& 0x1FF);
352 arm
->core_mode
= ARM_MODE_HANDLER
;
353 arm
->map
= armv7m_msp_reg_map
;
355 unsigned control
= buf_get_u32(armv7m
->core_cache
356 ->reg_list
[ARMV7M_CONTROL
].value
, 0, 2);
358 /* is this thread privileged? */
359 armv7m
->core_mode
= control
& 1;
360 arm
->core_mode
= armv7m
->core_mode
361 ? ARM_MODE_USER_THREAD
364 /* which stack is it using? */
366 arm
->map
= armv7m_psp_reg_map
;
368 arm
->map
= armv7m_msp_reg_map
;
370 armv7m
->exception_number
= 0;
373 LOG_DEBUG("entered debug state in core mode: %s at PC 0x%08" PRIx32
", target->state: %s",
374 armv7m_mode_strings
[armv7m
->core_mode
],
375 *(uint32_t *)(arm
->pc
->value
),
376 target_state_name(target
));
381 static int adapter_poll(struct target
*target
)
383 enum target_state state
;
384 struct hl_interface_s
*adapter
= target_to_adapter(target
);
385 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
387 state
= adapter
->layout
->api
->state(adapter
->fd
);
389 if (state
== TARGET_UNKNOWN
) {
390 LOG_ERROR("jtag status contains invalid mode value - communication failure");
391 return ERROR_TARGET_FAILURE
;
394 if (target
->state
== state
)
397 if (state
== TARGET_HALTED
) {
398 target
->state
= state
;
400 int retval
= adapter_debug_entry(target
);
401 if (retval
!= ERROR_OK
)
404 if (arm_semihosting(target
, &retval
) != 0)
407 target_call_event_callbacks(target
, TARGET_EVENT_HALTED
);
408 LOG_DEBUG("halted: PC: 0x%08x", buf_get_u32(armv7m
->arm
.pc
->value
, 0, 32));
414 static int adapter_assert_reset(struct target
*target
)
417 struct hl_interface_s
*adapter
= target_to_adapter(target
);
418 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
419 bool use_srst_fallback
= true;
421 LOG_DEBUG("%s", __func__
);
423 enum reset_types jtag_reset_config
= jtag_get_reset_config();
425 bool srst_asserted
= false;
427 if (jtag_reset_config
& RESET_SRST_NO_GATING
) {
428 jtag_add_reset(0, 1);
429 res
= adapter
->layout
->api
->assert_srst(adapter
->fd
, 0);
430 srst_asserted
= true;
433 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DHCSR
, DBGKEY
|C_DEBUGEN
);
435 /* only set vector catch if halt is requested */
436 if (target
->reset_halt
)
437 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DEMCR
, VC_CORERESET
);
439 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DEMCR
, 0);
441 if (jtag_reset_config
& RESET_HAS_SRST
) {
442 if (!srst_asserted
) {
443 jtag_add_reset(0, 1);
444 res
= adapter
->layout
->api
->assert_srst(adapter
->fd
, 0);
446 if (res
== ERROR_COMMAND_NOTFOUND
)
447 LOG_ERROR("Hardware srst not supported, falling back to software reset");
448 else if (res
== ERROR_OK
) {
449 /* hardware srst supported */
450 use_srst_fallback
= false;
454 if (use_srst_fallback
) {
455 /* stlink v1 api does not support hardware srst, so we use a software reset fallback */
456 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, NVIC_AIRCR
, AIRCR_VECTKEY
| AIRCR_SYSRESETREQ
);
459 res
= adapter
->layout
->api
->reset(adapter
->fd
);
464 /* registers are now invalid */
465 register_cache_invalidate(armv7m
->core_cache
);
467 if (target
->reset_halt
) {
468 target
->state
= TARGET_RESET
;
469 target
->debug_reason
= DBG_REASON_DBGRQ
;
471 target
->state
= TARGET_HALTED
;
477 static int adapter_deassert_reset(struct target
*target
)
480 struct hl_interface_s
*adapter
= target_to_adapter(target
);
482 enum reset_types jtag_reset_config
= jtag_get_reset_config();
484 LOG_DEBUG("%s", __func__
);
486 if (jtag_reset_config
& RESET_HAS_SRST
)
487 adapter
->layout
->api
->assert_srst(adapter
->fd
, 1);
489 /* virtual deassert reset, we need it for the internal
492 jtag_add_reset(0, 0);
494 if (!target
->reset_halt
) {
495 res
= target_resume(target
, 1, 0, 0, 0);
504 static int adapter_soft_reset_halt(struct target
*target
)
506 LOG_DEBUG("%s", __func__
);
510 static int adapter_halt(struct target
*target
)
513 struct hl_interface_s
*adapter
= target_to_adapter(target
);
515 LOG_DEBUG("%s", __func__
);
517 if (target
->state
== TARGET_HALTED
) {
518 LOG_DEBUG("target was already halted");
522 if (target
->state
== TARGET_UNKNOWN
)
523 LOG_WARNING("target was in unknown state when halt was requested");
525 res
= adapter
->layout
->api
->halt(adapter
->fd
);
530 target
->debug_reason
= DBG_REASON_DBGRQ
;
535 static int adapter_resume(struct target
*target
, int current
,
536 uint32_t address
, int handle_breakpoints
,
540 struct hl_interface_s
*adapter
= target_to_adapter(target
);
541 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
543 struct breakpoint
*breakpoint
= NULL
;
546 LOG_DEBUG("%s %d 0x%08x %d %d", __func__
, current
, address
,
547 handle_breakpoints
, debug_execution
);
549 if (target
->state
!= TARGET_HALTED
) {
550 LOG_WARNING("target not halted");
551 return ERROR_TARGET_NOT_HALTED
;
554 if (!debug_execution
) {
555 target_free_all_working_areas(target
);
556 cortex_m3_enable_breakpoints(target
);
557 cortex_m3_enable_watchpoints(target
);
562 buf_set_u32(pc
->value
, 0, 32, address
);
567 if (!breakpoint_find(target
, buf_get_u32(pc
->value
, 0, 32))
568 && !debug_execution
) {
569 armv7m_maybe_skip_bkpt_inst(target
, NULL
);
572 resume_pc
= buf_get_u32(pc
->value
, 0, 32);
574 armv7m_restore_context(target
);
576 /* registers are now invalid */
577 register_cache_invalidate(armv7m
->core_cache
);
579 /* the front-end may request us not to handle breakpoints */
580 if (handle_breakpoints
) {
581 /* Single step past breakpoint at current address */
582 breakpoint
= breakpoint_find(target
, resume_pc
);
584 LOG_DEBUG("unset breakpoint at 0x%8.8" PRIx32
" (ID: %d)",
586 breakpoint
->unique_id
);
587 cortex_m3_unset_breakpoint(target
, breakpoint
);
589 res
= adapter
->layout
->api
->step(adapter
->fd
);
594 cortex_m3_set_breakpoint(target
, breakpoint
);
598 res
= adapter
->layout
->api
->run(adapter
->fd
);
603 target
->state
= TARGET_RUNNING
;
604 target
->debug_reason
= DBG_REASON_NOTHALTED
;
606 target_call_event_callbacks(target
, TARGET_EVENT_RESUMED
);
611 static int adapter_step(struct target
*target
, int current
,
612 uint32_t address
, int handle_breakpoints
)
615 struct hl_interface_s
*adapter
= target_to_adapter(target
);
616 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
617 struct breakpoint
*breakpoint
= NULL
;
618 struct reg
*pc
= armv7m
->arm
.pc
;
619 bool bkpt_inst_found
= false;
621 LOG_DEBUG("%s", __func__
);
623 if (target
->state
!= TARGET_HALTED
) {
624 LOG_WARNING("target not halted");
625 return ERROR_TARGET_NOT_HALTED
;
629 buf_set_u32(pc
->value
, 0, 32, address
);
634 uint32_t pc_value
= buf_get_u32(pc
->value
, 0, 32);
636 /* the front-end may request us not to handle breakpoints */
637 if (handle_breakpoints
) {
638 breakpoint
= breakpoint_find(target
, pc_value
);
640 cortex_m3_unset_breakpoint(target
, breakpoint
);
643 armv7m_maybe_skip_bkpt_inst(target
, &bkpt_inst_found
);
645 target
->debug_reason
= DBG_REASON_SINGLESTEP
;
647 armv7m_restore_context(target
);
649 target_call_event_callbacks(target
, TARGET_EVENT_RESUMED
);
651 res
= adapter
->layout
->api
->step(adapter
->fd
);
656 /* registers are now invalid */
657 register_cache_invalidate(armv7m
->core_cache
);
660 cortex_m3_set_breakpoint(target
, breakpoint
);
662 adapter_debug_entry(target
);
663 target_call_event_callbacks(target
, TARGET_EVENT_HALTED
);
665 LOG_INFO("halted: PC: 0x%08x", buf_get_u32(armv7m
->arm
.pc
->value
, 0, 32));
670 static int adapter_read_memory(struct target
*target
, uint32_t address
,
671 uint32_t size
, uint32_t count
,
675 uint32_t buffer_threshold
= 128;
676 uint32_t addr_increment
= 4;
678 struct hl_interface_s
*adapter
= target_to_adapter(target
);
680 if (!count
|| !buffer
)
681 return ERROR_COMMAND_SYNTAX_ERROR
;
683 LOG_DEBUG("%s 0x%08x %d %d", __func__
, address
, size
, count
);
685 /* prepare byte count, buffer threshold
686 * and address increment for none 32bit access
690 buffer_threshold
= 64;
695 if (count
> buffer_threshold
)
696 c
= buffer_threshold
;
701 res
= adapter
->layout
->api
->read_mem8(adapter
->fd
,
704 res
= adapter
->layout
->api
->read_mem32(adapter
->fd
,
710 address
+= (c
* addr_increment
);
711 buffer
+= (c
* addr_increment
);
718 static int adapter_write_memory(struct target
*target
, uint32_t address
,
719 uint32_t size
, uint32_t count
,
720 const uint8_t *buffer
)
723 uint32_t buffer_threshold
= 128;
724 uint32_t addr_increment
= 4;
726 struct hl_interface_s
*adapter
= target_to_adapter(target
);
728 if (!count
|| !buffer
)
729 return ERROR_COMMAND_SYNTAX_ERROR
;
731 LOG_DEBUG("%s 0x%08x %d %d", __func__
, address
, size
, count
);
733 /* prepare byte count, buffer threshold
734 * and address increment for none 32bit access
738 buffer_threshold
= 64;
743 if (count
> buffer_threshold
)
744 c
= buffer_threshold
;
749 res
= adapter
->layout
->api
->write_mem8(adapter
->fd
,
752 res
= adapter
->layout
->api
->write_mem32(adapter
->fd
,
758 address
+= (c
* addr_increment
);
759 buffer
+= (c
* addr_increment
);
766 static int adapter_bulk_write_memory(struct target
*target
,
767 uint32_t address
, uint32_t count
,
768 const uint8_t *buffer
)
770 return adapter_write_memory(target
, address
, 4, count
, buffer
);
773 static const struct command_registration adapter_command_handlers
[] = {
775 .chain
= arm_command_handlers
,
777 COMMAND_REGISTRATION_DONE
780 struct target_type hla_target
= {
781 .name
= "hla_target",
782 .deprecated_name
= "stm32_stlink",
784 .init_target
= adapter_init_target
,
785 .target_create
= adapter_target_create
,
786 .examine
= cortex_m3_examine
,
787 .commands
= adapter_command_handlers
,
789 .poll
= adapter_poll
,
790 .arch_state
= armv7m_arch_state
,
792 .assert_reset
= adapter_assert_reset
,
793 .deassert_reset
= adapter_deassert_reset
,
794 .soft_reset_halt
= adapter_soft_reset_halt
,
796 .halt
= adapter_halt
,
797 .resume
= adapter_resume
,
798 .step
= adapter_step
,
800 .get_gdb_reg_list
= armv7m_get_gdb_reg_list
,
802 .read_memory
= adapter_read_memory
,
803 .write_memory
= adapter_write_memory
,
804 .bulk_write_memory
= adapter_bulk_write_memory
,
805 .checksum_memory
= armv7m_checksum_memory
,
806 .blank_check_memory
= armv7m_blank_check_memory
,
808 .run_algorithm
= armv7m_run_algorithm
,
809 .start_algorithm
= armv7m_start_algorithm
,
810 .wait_algorithm
= armv7m_wait_algorithm
,
812 .add_breakpoint
= cortex_m3_add_breakpoint
,
813 .remove_breakpoint
= cortex_m3_remove_breakpoint
,
814 .add_watchpoint
= cortex_m3_add_watchpoint
,
815 .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)