1 /***************************************************************************
2 * Copyright (C) 2017 by Intel Corporation
3 * Leandro Pereira <leandro.pereira@intel.com>
4 * Daniel Glöckner <dg@emlix.com>*
5 * Copyright (C) 2021 by Synopsys, Inc.
6 * Evgeniy Didin <didin@synopsys.com>
8 * SPDX-License-Identifier: GPL-2.0-or-later *
9 ***************************************************************************/
15 #include <helper/time_support.h>
16 #include <jtag/jtag.h>
18 #include "helper/log.h"
19 #include "helper/types.h"
21 #include "rtos_standard_stackings.h"
22 #include "target/target.h"
23 #include "target/target_type.h"
24 #include "target/armv7m.h"
25 #include "target/arc.h"
27 #define UNIMPLEMENTED 0xFFFFFFFFU
29 /* ARC specific defines */
30 #define ARC_AUX_SEC_BUILD_REG 0xdb
31 #define ARC_REG_NUM 38
33 /* ARM specific defines */
34 #define ARM_XPSR_OFFSET 28
36 struct zephyr_thread
{
37 uint32_t ptr
, next_ptr
;
39 uint32_t stack_pointer
;
53 OFFSET_T_USER_OPTIONS
,
55 OFFSET_T_STACK_POINTER
,
58 OFFSET_T_PREEMPT_FLOAT
,
63 struct zephyr_params
{
64 const char *target_name
;
66 uint8_t pointer_width
;
68 uint32_t offsets
[OFFSET_MAX
];
69 const struct rtos_register_stacking
*callee_saved_stacking
;
70 const struct rtos_register_stacking
*cpu_saved_nofp_stacking
;
71 const struct rtos_register_stacking
*cpu_saved_fp_stacking
;
72 int (*get_cpu_state
)(struct rtos
*rtos
, target_addr_t
*addr
,
73 struct zephyr_params
*params
,
74 struct rtos_reg
*callee_saved_reg_list
,
75 struct rtos_reg
**reg_list
, int *num_regs
);
78 static const struct stack_register_offset arm_callee_saved
[] = {
79 { ARMV7M_R13
, 32, 32 },
83 { ARMV7M_R7
, 12, 32 },
84 { ARMV7M_R8
, 16, 32 },
85 { ARMV7M_R9
, 20, 32 },
86 { ARMV7M_R10
, 24, 32 },
87 { ARMV7M_R11
, 28, 32 },
90 static const struct stack_register_offset arc_callee_saved
[] = {
108 static const struct rtos_register_stacking arm_callee_saved_stacking
= {
109 .stack_registers_size
= 36,
110 .stack_growth_direction
= -1,
111 .num_output_registers
= ARRAY_SIZE(arm_callee_saved
),
112 .register_offsets
= arm_callee_saved
,
115 static const struct rtos_register_stacking arc_callee_saved_stacking
= {
116 .stack_registers_size
= 64,
117 .stack_growth_direction
= -1,
118 .num_output_registers
= ARRAY_SIZE(arc_callee_saved
),
119 .register_offsets
= arc_callee_saved
,
122 static const struct stack_register_offset arm_cpu_saved
[] = {
123 { ARMV7M_R0
, 0, 32 },
124 { ARMV7M_R1
, 4, 32 },
125 { ARMV7M_R2
, 8, 32 },
126 { ARMV7M_R3
, 12, 32 },
127 { ARMV7M_R4
, -1, 32 },
128 { ARMV7M_R5
, -1, 32 },
129 { ARMV7M_R6
, -1, 32 },
130 { ARMV7M_R7
, -1, 32 },
131 { ARMV7M_R8
, -1, 32 },
132 { ARMV7M_R9
, -1, 32 },
133 { ARMV7M_R10
, -1, 32 },
134 { ARMV7M_R11
, -1, 32 },
135 { ARMV7M_R12
, 16, 32 },
136 { ARMV7M_R13
, -2, 32 },
137 { ARMV7M_R14
, 20, 32 },
138 { ARMV7M_PC
, 24, 32 },
139 { ARMV7M_xPSR
, 28, 32 },
142 static struct stack_register_offset arc_cpu_saved
[] = {
172 { ARC_ILINK
, -1, 32 },
174 { ARC_BLINK
, 0, 32 },
175 { ARC_LP_COUNT
, -1, 32 },
178 { ARC_LP_START
, -1, 32 },
179 { ARC_LP_END
, -1, 32 },
180 { ARC_STATUS32
, 4, 32 }
184 enum zephyr_symbol_values
{
186 ZEPHYR_VAL__KERNEL_OPENOCD_OFFSETS
,
187 ZEPHYR_VAL__KERNEL_OPENOCD_SIZE_T_SIZE
,
188 ZEPHYR_VAL__KERNEL_OPENOCD_NUM_OFFSETS
,
192 static int64_t zephyr_cortex_m_stack_align(struct target
*target
,
193 const uint8_t *stack_data
,
194 const struct rtos_register_stacking
*stacking
, int64_t stack_ptr
)
196 return rtos_cortex_m_stack_align(target
, stack_data
, stacking
,
197 stack_ptr
, ARM_XPSR_OFFSET
);
200 static const struct rtos_register_stacking arm_cpu_saved_nofp_stacking
= {
201 .stack_registers_size
= 32,
202 .stack_growth_direction
= -1,
203 .num_output_registers
= ARRAY_SIZE(arm_cpu_saved
),
204 .calculate_process_stack
= zephyr_cortex_m_stack_align
,
205 .register_offsets
= arm_cpu_saved
,
208 static const struct rtos_register_stacking arm_cpu_saved_fp_stacking
= {
209 .stack_registers_size
= 32 + 18 * 4,
210 .stack_growth_direction
= -1,
211 .num_output_registers
= ARRAY_SIZE(arm_cpu_saved
),
212 .calculate_process_stack
= zephyr_cortex_m_stack_align
,
213 .register_offsets
= arm_cpu_saved
,
216 /* stack_registers_size is 8 because besides caller registers
217 * there are only blink and Status32 registers on stack left */
218 static struct rtos_register_stacking arc_cpu_saved_stacking
= {
219 .stack_registers_size
= 8,
220 .stack_growth_direction
= -1,
221 .num_output_registers
= ARRAY_SIZE(arc_cpu_saved
),
222 .register_offsets
= arc_cpu_saved
,
225 /* ARCv2 specific implementation */
226 static int zephyr_get_arc_state(struct rtos
*rtos
, target_addr_t
*addr
,
227 struct zephyr_params
*params
,
228 struct rtos_reg
*callee_saved_reg_list
,
229 struct rtos_reg
**reg_list
, int *num_regs
)
232 uint32_t real_stack_addr
;
234 int num_callee_saved_regs
;
235 const struct rtos_register_stacking
*stacking
;
237 /* Getting real stack address from Kernel thread struct */
238 retval
= target_read_u32(rtos
->target
, *addr
, &real_stack_addr
);
239 if (retval
!= ERROR_OK
)
242 /* Getting callee registers */
243 retval
= rtos_generic_stack_read(rtos
->target
,
244 params
->callee_saved_stacking
,
245 real_stack_addr
, &callee_saved_reg_list
,
246 &num_callee_saved_regs
);
247 if (retval
!= ERROR_OK
)
250 stacking
= params
->cpu_saved_nofp_stacking
;
252 /* Getting blink and status32 registers */
253 retval
= rtos_generic_stack_read(rtos
->target
, stacking
,
254 real_stack_addr
+ num_callee_saved_regs
* 4,
256 if (retval
!= ERROR_OK
)
259 for (int i
= 0; i
< num_callee_saved_regs
; i
++)
260 buf_cpy(callee_saved_reg_list
[i
].value
,
261 (*reg_list
)[callee_saved_reg_list
[i
].number
].value
,
262 callee_saved_reg_list
[i
].size
);
264 /* The blink, sp, pc offsets in arc_cpu_saved structure may be changed,
265 * but the registers number shall not. So the next code searches the
266 * offsetst of these registers in arc_cpu_saved structure. */
267 unsigned short blink_offset
= 0, pc_offset
= 0, sp_offset
= 0;
268 for (size_t i
= 0; i
< ARRAY_SIZE(arc_cpu_saved
); i
++) {
269 if (arc_cpu_saved
[i
].number
== ARC_BLINK
)
271 if (arc_cpu_saved
[i
].number
== ARC_SP
)
273 if (arc_cpu_saved
[i
].number
== ARC_PC
)
277 if (blink_offset
== 0 || sp_offset
== 0 || pc_offset
== 0) {
278 LOG_ERROR("Basic registers offsets are missing, check <arc_cpu_saved> struct");
282 /* Put blink value into PC */
283 buf_cpy((*reg_list
)[blink_offset
].value
,
284 (*reg_list
)[pc_offset
].value
, sizeof((*reg_list
)[blink_offset
].value
));
286 /* Put address after callee/caller in SP. */
289 stack_top
= real_stack_addr
+ num_callee_saved_regs
* 4
290 + arc_cpu_saved_stacking
.stack_registers_size
;
291 buf_cpy(&stack_top
, (*reg_list
)[sp_offset
].value
, sizeof(stack_top
));
296 /* ARM Cortex-M-specific implementation */
297 static int zephyr_get_arm_state(struct rtos
*rtos
, target_addr_t
*addr
,
298 struct zephyr_params
*params
,
299 struct rtos_reg
*callee_saved_reg_list
,
300 struct rtos_reg
**reg_list
, int *num_regs
)
304 int num_callee_saved_regs
;
305 const struct rtos_register_stacking
*stacking
;
307 retval
= rtos_generic_stack_read(rtos
->target
,
308 params
->callee_saved_stacking
,
309 *addr
, &callee_saved_reg_list
,
310 &num_callee_saved_regs
);
311 if (retval
!= ERROR_OK
)
314 *addr
= target_buffer_get_u32(rtos
->target
,
315 callee_saved_reg_list
[0].value
);
317 if (params
->offsets
[OFFSET_T_PREEMPT_FLOAT
] != UNIMPLEMENTED
)
318 stacking
= params
->cpu_saved_fp_stacking
;
320 stacking
= params
->cpu_saved_nofp_stacking
;
322 retval
= rtos_generic_stack_read(rtos
->target
, stacking
, *addr
, reg_list
,
324 if (retval
!= ERROR_OK
)
327 for (int i
= 1; i
< num_callee_saved_regs
; i
++)
328 buf_cpy(callee_saved_reg_list
[i
].value
,
329 (*reg_list
)[callee_saved_reg_list
[i
].number
].value
,
330 callee_saved_reg_list
[i
].size
);
334 static struct zephyr_params zephyr_params_list
[] = {
336 .target_name
= "cortex_m",
338 .callee_saved_stacking
= &arm_callee_saved_stacking
,
339 .cpu_saved_nofp_stacking
= &arm_cpu_saved_nofp_stacking
,
340 .cpu_saved_fp_stacking
= &arm_cpu_saved_fp_stacking
,
341 .get_cpu_state
= &zephyr_get_arm_state
,
344 .target_name
= "hla_target",
346 .callee_saved_stacking
= &arm_callee_saved_stacking
,
347 .cpu_saved_nofp_stacking
= &arm_cpu_saved_nofp_stacking
,
348 .cpu_saved_fp_stacking
= &arm_cpu_saved_fp_stacking
,
349 .get_cpu_state
= &zephyr_get_arm_state
,
353 .target_name
= "arcv2",
355 .callee_saved_stacking
= &arc_callee_saved_stacking
,
356 .cpu_saved_nofp_stacking
= &arc_cpu_saved_stacking
,
357 .get_cpu_state
= &zephyr_get_arc_state
,
364 static const struct symbol_table_elem zephyr_symbol_list
[] = {
366 .symbol_name
= "_kernel",
370 .symbol_name
= "_kernel_openocd_offsets",
374 .symbol_name
= "_kernel_openocd_size_t_size",
378 .symbol_name
= "_kernel_openocd_num_offsets",
386 static bool zephyr_detect_rtos(struct target
*target
)
388 if (!target
->rtos
->symbols
) {
389 LOG_INFO("Zephyr: no symbols while detecting RTOS");
393 for (enum zephyr_symbol_values symbol
= ZEPHYR_VAL__KERNEL
;
394 symbol
!= ZEPHYR_VAL_COUNT
; symbol
++) {
395 LOG_INFO("Zephyr: does it have symbol %d (%s)?", symbol
,
396 target
->rtos
->symbols
[symbol
].optional
? "optional" : "mandatory");
398 if (target
->rtos
->symbols
[symbol
].optional
)
400 if (target
->rtos
->symbols
[symbol
].address
== 0)
404 LOG_INFO("Zephyr: all mandatory symbols found");
409 static int zephyr_create(struct target
*target
)
413 name
= target_type_name(target
);
415 LOG_INFO("Zephyr: looking for target: %s", name
);
417 /* ARC specific, check if EM target has security subsystem
418 * In case of ARC_HAS_SECURE zephyr option enabled
419 * the thread stack contains blink,sec_stat,status32 register
420 * values. If ARC_HAS_SECURE is disabled, only blink and status32
421 * register values are saved on stack. */
422 if (!strcmp(name
, "arcv2")) {
424 struct arc_common
*arc
= target_to_arc(target
);
425 /* Reading SEC_BUILD bcr */
426 CHECK_RETVAL(arc_jtag_read_aux_reg_one(&arc
->jtag_info
, ARC_AUX_SEC_BUILD_REG
, &value
));
428 LOG_DEBUG("ARC EM board has security subsystem, changing offsets");
429 arc_cpu_saved
[ARC_REG_NUM
- 1].offset
= 8;
430 /* After reading callee registers in stack
431 * now blink,sec_stat,status32 registers
433 arc_cpu_saved_stacking
.stack_registers_size
= 12;
437 for (struct zephyr_params
*p
= zephyr_params_list
; p
->target_name
; p
++) {
438 if (!strcmp(p
->target_name
, name
)) {
439 LOG_INFO("Zephyr: target known, params at %p", p
);
440 target
->rtos
->rtos_specific_params
= p
;
445 LOG_ERROR("Could not find target in Zephyr compatibility list");
449 struct zephyr_array
{
454 static void zephyr_array_init(struct zephyr_array
*array
)
460 static void zephyr_array_free(struct zephyr_array
*array
)
463 zephyr_array_init(array
);
466 static void *zephyr_array_append(struct zephyr_array
*array
, size_t size
)
468 if (!(array
->elements
% 16)) {
469 void *ptr
= realloc(array
->ptr
, (array
->elements
+ 16) * size
);
472 LOG_ERROR("Out of memory");
479 return (unsigned char *)array
->ptr
+ (array
->elements
++) * size
;
482 static void *zephyr_array_detach_ptr(struct zephyr_array
*array
)
484 void *ptr
= array
->ptr
;
486 zephyr_array_init(array
);
491 static uint32_t zephyr_kptr(const struct rtos
*rtos
, enum zephyr_offsets off
)
493 const struct zephyr_params
*params
= rtos
->rtos_specific_params
;
495 return rtos
->symbols
[ZEPHYR_VAL__KERNEL
].address
+ params
->offsets
[off
];
498 static int zephyr_fetch_thread(const struct rtos
*rtos
,
499 struct zephyr_thread
*thread
, uint32_t ptr
)
501 const struct zephyr_params
*param
= rtos
->rtos_specific_params
;
506 retval
= target_read_u32(rtos
->target
, ptr
+ param
->offsets
[OFFSET_T_ENTRY
],
508 if (retval
!= ERROR_OK
)
511 retval
= target_read_u32(rtos
->target
,
512 ptr
+ param
->offsets
[OFFSET_T_NEXT_THREAD
],
514 if (retval
!= ERROR_OK
)
517 retval
= target_read_u32(rtos
->target
,
518 ptr
+ param
->offsets
[OFFSET_T_STACK_POINTER
],
519 &thread
->stack_pointer
);
520 if (retval
!= ERROR_OK
)
523 retval
= target_read_u8(rtos
->target
, ptr
+ param
->offsets
[OFFSET_T_STATE
],
525 if (retval
!= ERROR_OK
)
528 retval
= target_read_u8(rtos
->target
,
529 ptr
+ param
->offsets
[OFFSET_T_USER_OPTIONS
],
530 &thread
->user_options
);
531 if (retval
!= ERROR_OK
)
535 retval
= target_read_u8(rtos
->target
,
536 ptr
+ param
->offsets
[OFFSET_T_PRIO
], &prio
);
537 if (retval
!= ERROR_OK
)
541 thread
->name
[0] = '\0';
542 if (param
->offsets
[OFFSET_T_NAME
] != UNIMPLEMENTED
) {
543 retval
= target_read_buffer(rtos
->target
,
544 ptr
+ param
->offsets
[OFFSET_T_NAME
],
545 sizeof(thread
->name
) - 1, (uint8_t *)thread
->name
);
546 if (retval
!= ERROR_OK
)
549 thread
->name
[sizeof(thread
->name
) - 1] = '\0';
552 LOG_DEBUG("Fetched thread%" PRIx32
": {entry@0x%" PRIx32
553 ", state=%" PRIu8
", useropts=%" PRIu8
", prio=%" PRId8
"}",
554 ptr
, thread
->entry
, thread
->state
, thread
->user_options
, thread
->prio
);
559 static int zephyr_fetch_thread_list(struct rtos
*rtos
, uint32_t current_thread
)
561 struct zephyr_array thread_array
;
562 struct zephyr_thread thread
;
563 struct thread_detail
*td
;
564 int64_t curr_id
= -1;
568 retval
= target_read_u32(rtos
->target
, zephyr_kptr(rtos
, OFFSET_K_THREADS
),
570 if (retval
!= ERROR_OK
) {
571 LOG_ERROR("Could not fetch current thread pointer");
575 zephyr_array_init(&thread_array
);
577 for (; curr
; curr
= thread
.next_ptr
) {
578 retval
= zephyr_fetch_thread(rtos
, &thread
, curr
);
579 if (retval
!= ERROR_OK
)
582 td
= zephyr_array_append(&thread_array
, sizeof(*td
));
586 td
->threadid
= thread
.ptr
;
590 td
->thread_name_str
= strdup(thread
.name
);
592 td
->thread_name_str
= alloc_printf("thr_%" PRIx32
"_%" PRIx32
,
593 thread
.entry
, thread
.ptr
);
594 td
->extra_info_str
= alloc_printf("prio:%" PRId8
",useropts:%" PRIu8
,
595 thread
.prio
, thread
.user_options
);
596 if (!td
->thread_name_str
|| !td
->extra_info_str
)
599 if (td
->threadid
== current_thread
)
600 curr_id
= (int64_t)thread_array
.elements
- 1;
603 LOG_DEBUG("Got information for %zu threads", thread_array
.elements
);
605 rtos_free_threadlist(rtos
);
607 rtos
->thread_count
= (int)thread_array
.elements
;
608 rtos
->thread_details
= zephyr_array_detach_ptr(&thread_array
);
610 rtos
->current_threadid
= curr_id
;
611 rtos
->current_thread
= current_thread
;
616 td
= thread_array
.ptr
;
617 for (size_t i
= 0; i
< thread_array
.elements
; i
++) {
618 free(td
[i
].thread_name_str
);
619 free(td
[i
].extra_info_str
);
622 zephyr_array_free(&thread_array
);
627 static int zephyr_update_threads(struct rtos
*rtos
)
629 struct zephyr_params
*param
;
632 if (!rtos
->rtos_specific_params
)
635 param
= (struct zephyr_params
*)rtos
->rtos_specific_params
;
637 if (!rtos
->symbols
) {
638 LOG_ERROR("No symbols for Zephyr");
642 if (rtos
->symbols
[ZEPHYR_VAL__KERNEL
].address
== 0) {
643 LOG_ERROR("Can't obtain kernel struct from Zephyr");
647 if (rtos
->symbols
[ZEPHYR_VAL__KERNEL_OPENOCD_OFFSETS
].address
== 0) {
648 LOG_ERROR("Please build Zephyr with CONFIG_OPENOCD option set");
652 retval
= target_read_u8(rtos
->target
,
653 rtos
->symbols
[ZEPHYR_VAL__KERNEL_OPENOCD_SIZE_T_SIZE
].address
,
655 if (retval
!= ERROR_OK
) {
656 LOG_ERROR("Couldn't determine size of size_t from host");
660 if (param
->size_width
!= 4) {
661 LOG_ERROR("Only size_t of 4 bytes are supported");
665 if (rtos
->symbols
[ZEPHYR_VAL__KERNEL_OPENOCD_NUM_OFFSETS
].address
) {
666 retval
= target_read_u32(rtos
->target
,
667 rtos
->symbols
[ZEPHYR_VAL__KERNEL_OPENOCD_NUM_OFFSETS
].address
,
668 ¶m
->num_offsets
);
669 if (retval
!= ERROR_OK
) {
670 LOG_ERROR("Couldn't not fetch number of offsets from Zephyr");
674 if (param
->num_offsets
<= OFFSET_T_STACK_POINTER
) {
675 LOG_ERROR("Number of offsets too small");
679 retval
= target_read_u32(rtos
->target
,
680 rtos
->symbols
[ZEPHYR_VAL__KERNEL_OPENOCD_OFFSETS
].address
,
681 ¶m
->offsets
[OFFSET_VERSION
]);
682 if (retval
!= ERROR_OK
) {
683 LOG_ERROR("Couldn't not fetch offsets from Zephyr");
687 if (param
->offsets
[OFFSET_VERSION
] > 1) {
688 LOG_ERROR("Unexpected OpenOCD support version %" PRIu32
,
689 param
->offsets
[OFFSET_VERSION
]);
692 switch (param
->offsets
[OFFSET_VERSION
]) {
694 param
->num_offsets
= OFFSET_T_STACK_POINTER
+ 1;
697 param
->num_offsets
= OFFSET_T_COOP_FLOAT
+ 1;
701 /* We can fetch the whole array for version 0, as they're supposed
704 address
= rtos
->symbols
[ZEPHYR_VAL__KERNEL_OPENOCD_OFFSETS
].address
;
705 for (size_t i
= 0; i
< OFFSET_MAX
; i
++, address
+= param
->size_width
) {
706 if (i
>= param
->num_offsets
) {
707 param
->offsets
[i
] = UNIMPLEMENTED
;
711 retval
= target_read_u32(rtos
->target
, address
, ¶m
->offsets
[i
]);
712 if (retval
!= ERROR_OK
) {
713 LOG_ERROR("Could not fetch offsets from Zephyr");
718 LOG_DEBUG("Zephyr OpenOCD support version %" PRId32
,
719 param
->offsets
[OFFSET_VERSION
]);
721 uint32_t current_thread
;
722 retval
= target_read_u32(rtos
->target
,
723 zephyr_kptr(rtos
, OFFSET_K_CURR_THREAD
), ¤t_thread
);
724 if (retval
!= ERROR_OK
) {
725 LOG_ERROR("Could not obtain current thread ID");
729 retval
= zephyr_fetch_thread_list(rtos
, current_thread
);
730 if (retval
!= ERROR_OK
) {
731 LOG_ERROR("Could not obtain thread list");
738 static int zephyr_get_thread_reg_list(struct rtos
*rtos
, int64_t thread_id
,
739 struct rtos_reg
**reg_list
, int *num_regs
)
741 struct zephyr_params
*params
;
742 struct rtos_reg
*callee_saved_reg_list
= NULL
;
746 LOG_INFO("Getting thread %" PRId64
" reg list", thread_id
);
754 params
= rtos
->rtos_specific_params
;
758 addr
= thread_id
+ params
->offsets
[OFFSET_T_STACK_POINTER
]
759 - params
->callee_saved_stacking
->register_offsets
[0].offset
;
761 retval
= params
->get_cpu_state(rtos
, &addr
, params
, callee_saved_reg_list
, reg_list
, num_regs
);
763 free(callee_saved_reg_list
);
768 static int zephyr_get_symbol_list_to_lookup(struct symbol_table_elem
**symbol_list
)
770 *symbol_list
= malloc(sizeof(zephyr_symbol_list
));
772 LOG_ERROR("Out of memory");
776 memcpy(*symbol_list
, zephyr_symbol_list
, sizeof(zephyr_symbol_list
));
780 struct rtos_type zephyr_rtos
= {
783 .detect_rtos
= zephyr_detect_rtos
,
784 .create
= zephyr_create
,
785 .update_threads
= zephyr_update_threads
,
786 .get_thread_reg_list
= zephyr_get_thread_reg_list
,
787 .get_symbol_list_to_lookup
= zephyr_get_symbol_list_to_lookup
,
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)