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 uint32_t num
, uint32_t *value
)
53 struct hl_interface_s
*adapter
= target_to_adapter(target
);
55 LOG_DEBUG("%s", __func__
);
57 /* NOTE: we "know" here that the register identifiers used
58 * in the v7m header match the Cortex-M3 Debug Core Register
59 * Selector values for R0..R15, xPSR, MSP, and PSP.
63 /* read a normal core register */
64 retval
= adapter
->layout
->api
->read_reg(adapter
->fd
, num
, value
);
66 if (retval
!= ERROR_OK
) {
67 LOG_ERROR("JTAG failure %i", retval
);
68 return ERROR_JTAG_DEVICE_ERROR
;
70 LOG_DEBUG("load from core reg %i value 0x%" PRIx32
"", (int)num
, *value
);
79 /* Floating-point Status and Registers */
80 retval
= target_write_u32(target
, ARMV7M_SCS_DCRSR
, 33);
81 if (retval
!= ERROR_OK
)
83 retval
= target_read_u32(target
, ARMV7M_SCS_DCRDR
, value
);
84 if (retval
!= ERROR_OK
)
86 LOG_DEBUG("load from core reg %i value 0x%" PRIx32
"", (int)num
, *value
);
89 case ARMV7M_S0
... ARMV7M_S31
:
90 /* Floating-point Status and Registers */
91 retval
= target_write_u32(target
, ARMV7M_SCS_DCRSR
, num
-ARMV7M_S0
+64);
92 if (retval
!= ERROR_OK
)
94 retval
= target_read_u32(target
, ARMV7M_SCS_DCRDR
, value
);
95 if (retval
!= ERROR_OK
)
97 LOG_DEBUG("load from core reg %i value 0x%" PRIx32
"", (int)num
, *value
);
100 case ARMV7M_D0
... ARMV7M_D15
:
106 case ARMV7M_FAULTMASK
:
108 /* Cortex-M3 packages these four registers as bitfields
109 * in one Debug Core register. So say r0 and r2 docs;
110 * it was removed from r1 docs, but still works.
112 retval
= adapter
->layout
->api
->read_reg(adapter
->fd
, 20, value
);
113 if (retval
!= ERROR_OK
)
118 *value
= buf_get_u32((uint8_t *) value
, 0, 1);
122 *value
= buf_get_u32((uint8_t *) value
, 8, 8);
125 case ARMV7M_FAULTMASK
:
126 *value
= buf_get_u32((uint8_t *) value
, 16, 1);
130 *value
= buf_get_u32((uint8_t *) value
, 24, 2);
134 LOG_DEBUG("load from special reg %i value 0x%" PRIx32
"",
139 return ERROR_COMMAND_SYNTAX_ERROR
;
145 static int adapter_store_core_reg_u32(struct target
*target
,
146 uint32_t num
, uint32_t value
)
150 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
151 struct hl_interface_s
*adapter
= target_to_adapter(target
);
153 LOG_DEBUG("%s", __func__
);
155 #ifdef ARMV7_GDB_HACKS
156 /* If the LR register is being modified, make sure it will put us
157 * in "thumb" mode, or an INVSTATE exception will occur. This is a
158 * hack to deal with the fact that gdb will sometimes "forge"
159 * return addresses, and doesn't set the LSB correctly (i.e., when
160 * printing expressions containing function calls, it sets LR = 0.)
161 * Valid exception return codes have bit 0 set too.
163 if (num
== ARMV7M_R14
)
167 /* NOTE: we "know" here that the register identifiers used
168 * in the v7m header match the Cortex-M3 Debug Core Register
169 * Selector values for R0..R15, xPSR, MSP, and PSP.
173 retval
= adapter
->layout
->api
->write_reg(adapter
->fd
, num
, value
);
175 if (retval
!= ERROR_OK
) {
178 LOG_ERROR("JTAG failure");
179 r
= armv7m
->core_cache
->reg_list
+ num
;
181 return ERROR_JTAG_DEVICE_ERROR
;
183 LOG_DEBUG("write core reg %i value 0x%" PRIx32
"", (int)num
, value
);
191 /* Floating-point Status and Registers */
192 retval
= target_write_u32(target
, ARMV7M_SCS_DCRDR
, value
);
193 if (retval
!= ERROR_OK
)
195 retval
= target_write_u32(target
, ARMV7M_SCS_DCRSR
, 33 | (1<<16));
196 if (retval
!= ERROR_OK
)
198 LOG_DEBUG("write core reg %i value 0x%" PRIx32
"", (int)num
, value
);
201 case ARMV7M_S0
... ARMV7M_S31
:
202 /* Floating-point Status and Registers */
203 retval
= target_write_u32(target
, ARMV7M_SCS_DCRDR
, value
);
204 if (retval
!= ERROR_OK
)
206 retval
= target_write_u32(target
, ARMV7M_SCS_DCRSR
, (num
-ARMV7M_S0
+64) | (1<<16));
207 if (retval
!= ERROR_OK
)
209 LOG_DEBUG("write core reg %i value 0x%" PRIx32
"", (int)num
, value
);
212 case ARMV7M_D0
... ARMV7M_D15
:
217 case ARMV7M_FAULTMASK
:
219 /* Cortex-M3 packages these four registers as bitfields
220 * in one Debug Core register. So say r0 and r2 docs;
221 * it was removed from r1 docs, but still works.
224 adapter
->layout
->api
->read_reg(adapter
->fd
, 20, ®
);
228 buf_set_u32((uint8_t *) ®
, 0, 1, value
);
232 buf_set_u32((uint8_t *) ®
, 8, 8, value
);
235 case ARMV7M_FAULTMASK
:
236 buf_set_u32((uint8_t *) ®
, 16, 1, value
);
240 buf_set_u32((uint8_t *) ®
, 24, 2, value
);
244 adapter
->layout
->api
->write_reg(adapter
->fd
, 20, reg
);
246 LOG_DEBUG("write special reg %i value 0x%" PRIx32
" ", (int)num
, value
);
250 return ERROR_COMMAND_SYNTAX_ERROR
;
256 static int adapter_examine_debug_reason(struct target
*target
)
258 if ((target
->debug_reason
!= DBG_REASON_DBGRQ
)
259 && (target
->debug_reason
!= DBG_REASON_SINGLESTEP
)) {
260 target
->debug_reason
= DBG_REASON_BREAKPOINT
;
266 static int adapter_init_arch_info(struct target
*target
,
267 struct cortex_m3_common
*cortex_m3
,
268 struct jtag_tap
*tap
)
270 struct armv7m_common
*armv7m
;
272 LOG_DEBUG("%s", __func__
);
274 armv7m
= &cortex_m3
->armv7m
;
275 armv7m_init_arch_info(target
, armv7m
);
277 armv7m
->load_core_reg_u32
= adapter_load_core_reg_u32
;
278 armv7m
->store_core_reg_u32
= adapter_store_core_reg_u32
;
280 armv7m
->examine_debug_reason
= adapter_examine_debug_reason
;
281 armv7m
->stlink
= true;
286 static int adapter_init_target(struct command_context
*cmd_ctx
,
287 struct target
*target
)
289 LOG_DEBUG("%s", __func__
);
291 armv7m_build_reg_cache(target
);
296 static int adapter_target_create(struct target
*target
,
299 LOG_DEBUG("%s", __func__
);
301 struct cortex_m3_common
*cortex_m3
= calloc(1, sizeof(struct cortex_m3_common
));
304 return ERROR_COMMAND_SYNTAX_ERROR
;
306 adapter_init_arch_info(target
, cortex_m3
, target
->tap
);
311 static int adapter_load_context(struct target
*target
)
313 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
314 int num_regs
= armv7m
->core_cache
->num_regs
;
316 for (int i
= 0; i
< num_regs
; i
++) {
317 if (!armv7m
->core_cache
->reg_list
[i
].valid
)
318 armv7m
->read_core_reg(target
, i
);
324 static int adapter_debug_entry(struct target
*target
)
326 struct hl_interface_s
*adapter
= target_to_adapter(target
);
327 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
328 struct arm
*arm
= &armv7m
->arm
;
333 retval
= armv7m
->examine_debug_reason(target
);
334 if (retval
!= ERROR_OK
)
337 adapter_load_context(target
);
339 /* make sure we clear the vector catch bit */
340 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DEMCR
, TRCENA
);
342 r
= armv7m
->core_cache
->reg_list
+ ARMV7M_xPSR
;
343 xPSR
= buf_get_u32(r
->value
, 0, 32);
345 /* Are we in an exception handler */
347 armv7m
->exception_number
= (xPSR
& 0x1FF);
349 arm
->core_mode
= ARM_MODE_HANDLER
;
350 arm
->map
= armv7m_msp_reg_map
;
352 unsigned control
= buf_get_u32(arm
->core_cache
353 ->reg_list
[ARMV7M_CONTROL
].value
, 0, 2);
355 /* is this thread privileged? */
356 arm
->core_mode
= control
& 1
357 ? ARM_MODE_USER_THREAD
360 /* which stack is it using? */
362 arm
->map
= armv7m_psp_reg_map
;
364 arm
->map
= armv7m_msp_reg_map
;
366 armv7m
->exception_number
= 0;
369 LOG_DEBUG("entered debug state in core mode: %s at PC 0x%08" PRIx32
", target->state: %s",
370 arm_mode_name(arm
->core_mode
),
371 *(uint32_t *)(arm
->pc
->value
),
372 target_state_name(target
));
377 static int adapter_poll(struct target
*target
)
379 enum target_state state
;
380 struct hl_interface_s
*adapter
= target_to_adapter(target
);
381 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
383 state
= adapter
->layout
->api
->state(adapter
->fd
);
385 if (state
== TARGET_UNKNOWN
) {
386 LOG_ERROR("jtag status contains invalid mode value - communication failure");
387 return ERROR_TARGET_FAILURE
;
390 if (target
->state
== state
)
393 if (state
== TARGET_HALTED
) {
394 target
->state
= state
;
396 int retval
= adapter_debug_entry(target
);
397 if (retval
!= ERROR_OK
)
400 if (arm_semihosting(target
, &retval
) != 0)
403 target_call_event_callbacks(target
, TARGET_EVENT_HALTED
);
404 LOG_DEBUG("halted: PC: 0x%08x", buf_get_u32(armv7m
->arm
.pc
->value
, 0, 32));
410 static int adapter_assert_reset(struct target
*target
)
413 struct hl_interface_s
*adapter
= target_to_adapter(target
);
414 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
415 bool use_srst_fallback
= true;
417 LOG_DEBUG("%s", __func__
);
419 enum reset_types jtag_reset_config
= jtag_get_reset_config();
421 bool srst_asserted
= false;
423 if (jtag_reset_config
& RESET_SRST_NO_GATING
) {
424 jtag_add_reset(0, 1);
425 res
= adapter
->layout
->api
->assert_srst(adapter
->fd
, 0);
426 srst_asserted
= true;
429 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DHCSR
, DBGKEY
|C_DEBUGEN
);
431 /* only set vector catch if halt is requested */
432 if (target
->reset_halt
)
433 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DEMCR
, TRCENA
|VC_CORERESET
);
435 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, DCB_DEMCR
, TRCENA
);
437 if (jtag_reset_config
& RESET_HAS_SRST
) {
438 if (!srst_asserted
) {
439 jtag_add_reset(0, 1);
440 res
= adapter
->layout
->api
->assert_srst(adapter
->fd
, 0);
442 if (res
== ERROR_COMMAND_NOTFOUND
)
443 LOG_ERROR("Hardware srst not supported, falling back to software reset");
444 else if (res
== ERROR_OK
) {
445 /* hardware srst supported */
446 use_srst_fallback
= false;
450 if (use_srst_fallback
) {
451 /* stlink v1 api does not support hardware srst, so we use a software reset fallback */
452 adapter
->layout
->api
->write_debug_reg(adapter
->fd
, NVIC_AIRCR
, AIRCR_VECTKEY
| AIRCR_SYSRESETREQ
);
455 res
= adapter
->layout
->api
->reset(adapter
->fd
);
460 /* registers are now invalid */
461 register_cache_invalidate(armv7m
->core_cache
);
463 if (target
->reset_halt
) {
464 target
->state
= TARGET_RESET
;
465 target
->debug_reason
= DBG_REASON_DBGRQ
;
467 target
->state
= TARGET_HALTED
;
473 static int adapter_deassert_reset(struct target
*target
)
476 struct hl_interface_s
*adapter
= target_to_adapter(target
);
478 enum reset_types jtag_reset_config
= jtag_get_reset_config();
480 LOG_DEBUG("%s", __func__
);
482 if (jtag_reset_config
& RESET_HAS_SRST
)
483 adapter
->layout
->api
->assert_srst(adapter
->fd
, 1);
485 /* virtual deassert reset, we need it for the internal
488 jtag_add_reset(0, 0);
490 if (!target
->reset_halt
) {
491 res
= target_resume(target
, 1, 0, 0, 0);
500 static int adapter_soft_reset_halt(struct target
*target
)
502 LOG_DEBUG("%s", __func__
);
506 static int adapter_halt(struct target
*target
)
509 struct hl_interface_s
*adapter
= target_to_adapter(target
);
511 LOG_DEBUG("%s", __func__
);
513 if (target
->state
== TARGET_HALTED
) {
514 LOG_DEBUG("target was already halted");
518 if (target
->state
== TARGET_UNKNOWN
)
519 LOG_WARNING("target was in unknown state when halt was requested");
521 res
= adapter
->layout
->api
->halt(adapter
->fd
);
526 target
->debug_reason
= DBG_REASON_DBGRQ
;
531 static int adapter_resume(struct target
*target
, int current
,
532 uint32_t address
, int handle_breakpoints
,
536 struct hl_interface_s
*adapter
= target_to_adapter(target
);
537 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
539 struct breakpoint
*breakpoint
= NULL
;
542 LOG_DEBUG("%s %d 0x%08x %d %d", __func__
, current
, address
,
543 handle_breakpoints
, debug_execution
);
545 if (target
->state
!= TARGET_HALTED
) {
546 LOG_WARNING("target not halted");
547 return ERROR_TARGET_NOT_HALTED
;
550 if (!debug_execution
) {
551 target_free_all_working_areas(target
);
552 cortex_m3_enable_breakpoints(target
);
553 cortex_m3_enable_watchpoints(target
);
558 buf_set_u32(pc
->value
, 0, 32, address
);
563 if (!breakpoint_find(target
, buf_get_u32(pc
->value
, 0, 32))
564 && !debug_execution
) {
565 armv7m_maybe_skip_bkpt_inst(target
, NULL
);
568 resume_pc
= buf_get_u32(pc
->value
, 0, 32);
570 /* write any user vector flags */
571 res
= target_write_u32(target
, DCB_DEMCR
, TRCENA
| armv7m
->demcr
);
575 armv7m_restore_context(target
);
577 /* registers are now invalid */
578 register_cache_invalidate(armv7m
->core_cache
);
580 /* the front-end may request us not to handle breakpoints */
581 if (handle_breakpoints
) {
582 /* Single step past breakpoint at current address */
583 breakpoint
= breakpoint_find(target
, resume_pc
);
585 LOG_DEBUG("unset breakpoint at 0x%8.8" PRIx32
" (ID: %d)",
587 breakpoint
->unique_id
);
588 cortex_m3_unset_breakpoint(target
, breakpoint
);
590 res
= adapter
->layout
->api
->step(adapter
->fd
);
595 cortex_m3_set_breakpoint(target
, breakpoint
);
599 res
= adapter
->layout
->api
->run(adapter
->fd
);
604 target
->state
= TARGET_RUNNING
;
605 target
->debug_reason
= DBG_REASON_NOTHALTED
;
607 target_call_event_callbacks(target
, TARGET_EVENT_RESUMED
);
612 static int adapter_step(struct target
*target
, int current
,
613 uint32_t address
, int handle_breakpoints
)
616 struct hl_interface_s
*adapter
= target_to_adapter(target
);
617 struct armv7m_common
*armv7m
= target_to_armv7m(target
);
618 struct breakpoint
*breakpoint
= NULL
;
619 struct reg
*pc
= armv7m
->arm
.pc
;
620 bool bkpt_inst_found
= false;
622 LOG_DEBUG("%s", __func__
);
624 if (target
->state
!= TARGET_HALTED
) {
625 LOG_WARNING("target not halted");
626 return ERROR_TARGET_NOT_HALTED
;
630 buf_set_u32(pc
->value
, 0, 32, address
);
635 uint32_t pc_value
= buf_get_u32(pc
->value
, 0, 32);
637 /* the front-end may request us not to handle breakpoints */
638 if (handle_breakpoints
) {
639 breakpoint
= breakpoint_find(target
, pc_value
);
641 cortex_m3_unset_breakpoint(target
, breakpoint
);
644 armv7m_maybe_skip_bkpt_inst(target
, &bkpt_inst_found
);
646 target
->debug_reason
= DBG_REASON_SINGLESTEP
;
648 armv7m_restore_context(target
);
650 target_call_event_callbacks(target
, TARGET_EVENT_RESUMED
);
652 res
= adapter
->layout
->api
->step(adapter
->fd
);
657 /* registers are now invalid */
658 register_cache_invalidate(armv7m
->core_cache
);
661 cortex_m3_set_breakpoint(target
, breakpoint
);
663 adapter_debug_entry(target
);
664 target_call_event_callbacks(target
, TARGET_EVENT_HALTED
);
666 LOG_INFO("halted: PC: 0x%08x", buf_get_u32(armv7m
->arm
.pc
->value
, 0, 32));
671 static int adapter_read_memory(struct target
*target
, uint32_t address
,
672 uint32_t size
, uint32_t count
,
675 struct hl_interface_s
*adapter
= target_to_adapter(target
);
677 uint32_t buffer_threshold
= (adapter
->param
.max_buffer
/ 4);
678 uint32_t addr_increment
= 4;
681 if (!count
|| !buffer
)
682 return ERROR_COMMAND_SYNTAX_ERROR
;
684 LOG_DEBUG("%s 0x%08x %d %d", __func__
, address
, size
, count
);
686 /* prepare byte count, buffer threshold
687 * and address increment for none 32bit access
691 buffer_threshold
= (adapter
->param
.max_buffer
/ 4) / 2;
696 if (count
> buffer_threshold
)
697 c
= buffer_threshold
;
702 res
= adapter
->layout
->api
->read_mem8(adapter
->fd
,
705 res
= adapter
->layout
->api
->read_mem32(adapter
->fd
,
711 address
+= (c
* addr_increment
);
712 buffer
+= (c
* addr_increment
);
719 static int adapter_write_memory(struct target
*target
, uint32_t address
,
720 uint32_t size
, uint32_t count
,
721 const uint8_t *buffer
)
723 struct hl_interface_s
*adapter
= target_to_adapter(target
);
725 uint32_t buffer_threshold
= (adapter
->param
.max_buffer
/ 4);
726 uint32_t addr_increment
= 4;
729 if (!count
|| !buffer
)
730 return ERROR_COMMAND_SYNTAX_ERROR
;
732 LOG_DEBUG("%s 0x%08x %d %d", __func__
, address
, size
, count
);
734 /* prepare byte count, buffer threshold
735 * and address increment for none 32bit access
739 buffer_threshold
= (adapter
->param
.max_buffer
/ 4) / 2;
744 if (count
> buffer_threshold
)
745 c
= buffer_threshold
;
750 res
= adapter
->layout
->api
->write_mem8(adapter
->fd
,
753 res
= adapter
->layout
->api
->write_mem32(adapter
->fd
,
759 address
+= (c
* addr_increment
);
760 buffer
+= (c
* addr_increment
);
767 static int adapter_bulk_write_memory(struct target
*target
,
768 uint32_t address
, uint32_t count
,
769 const uint8_t *buffer
)
771 return adapter_write_memory(target
, address
, 4, count
, buffer
);
774 static const struct command_registration adapter_command_handlers
[] = {
776 .chain
= arm_command_handlers
,
778 COMMAND_REGISTRATION_DONE
781 struct target_type hla_target
= {
782 .name
= "hla_target",
783 .deprecated_name
= "stm32_stlink",
785 .init_target
= adapter_init_target
,
786 .target_create
= adapter_target_create
,
787 .examine
= cortex_m3_examine
,
788 .commands
= adapter_command_handlers
,
790 .poll
= adapter_poll
,
791 .arch_state
= armv7m_arch_state
,
793 .assert_reset
= adapter_assert_reset
,
794 .deassert_reset
= adapter_deassert_reset
,
795 .soft_reset_halt
= adapter_soft_reset_halt
,
797 .halt
= adapter_halt
,
798 .resume
= adapter_resume
,
799 .step
= adapter_step
,
801 .get_gdb_reg_list
= armv7m_get_gdb_reg_list
,
803 .read_memory
= adapter_read_memory
,
804 .write_memory
= adapter_write_memory
,
805 .bulk_write_memory
= adapter_bulk_write_memory
,
806 .checksum_memory
= armv7m_checksum_memory
,
807 .blank_check_memory
= armv7m_blank_check_memory
,
809 .run_algorithm
= armv7m_run_algorithm
,
810 .start_algorithm
= armv7m_start_algorithm
,
811 .wait_algorithm
= armv7m_wait_algorithm
,
813 .add_breakpoint
= cortex_m3_add_breakpoint
,
814 .remove_breakpoint
= cortex_m3_remove_breakpoint
,
815 .add_watchpoint
= cortex_m3_add_watchpoint
,
816 .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)