1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 /***************************************************************************
4 * Copyright (C) 2011 by Broadcom Corporation *
5 * Evan Hunter - ehunter@broadcom.com *
6 ***************************************************************************/
13 #include "target/target.h"
14 #include "helper/log.h"
15 #include "helper/binarybuffer.h"
16 #include "server/gdb_server.h"
19 extern struct rtos_type freertos_rtos
;
20 extern struct rtos_type threadx_rtos
;
21 extern struct rtos_type ecos_rtos
;
22 extern struct rtos_type linux_rtos
;
23 extern struct rtos_type chibios_rtos
;
24 extern struct rtos_type chromium_ec_rtos
;
25 extern struct rtos_type embkernel_rtos
;
26 extern struct rtos_type mqx_rtos
;
27 extern struct rtos_type ucos_iii_rtos
;
28 extern struct rtos_type nuttx_rtos
;
29 extern struct rtos_type hwthread_rtos
;
30 extern struct rtos_type riot_rtos
;
31 extern struct rtos_type zephyr_rtos
;
33 static struct rtos_type
*rtos_types
[] = {
46 /* keep this as last, as it always matches with rtos auto */
51 static int rtos_try_next(struct target
*target
);
53 int rtos_thread_packet(struct connection
*connection
, const char *packet
, int packet_size
);
55 int rtos_smp_init(struct target
*target
)
57 if (target
->rtos
->type
->smp_init
)
58 return target
->rtos
->type
->smp_init(target
);
59 return ERROR_TARGET_INIT_FAILED
;
62 static int rtos_target_for_threadid(struct connection
*connection
, int64_t threadid
, struct target
**t
)
64 struct target
*curr
= get_target_from_connection(connection
);
71 static int os_alloc(struct target
*target
, struct rtos_type
*ostype
)
73 struct rtos
*os
= target
->rtos
= calloc(1, sizeof(struct rtos
));
79 os
->current_threadid
= -1;
80 os
->current_thread
= 0;
84 /* RTOS drivers can override the packet handler in _create(). */
85 os
->gdb_thread_packet
= rtos_thread_packet
;
86 os
->gdb_target_for_threadid
= rtos_target_for_threadid
;
91 static void os_free(struct target
*target
)
96 free(target
->rtos
->symbols
);
101 static int os_alloc_create(struct target
*target
, struct rtos_type
*ostype
)
103 int ret
= os_alloc(target
, ostype
);
106 ret
= target
->rtos
->type
->create(target
);
114 int rtos_create(struct jim_getopt_info
*goi
, struct target
*target
)
121 if (!goi
->isconfigure
&& goi
->argc
!= 0) {
122 Jim_WrongNumArgs(goi
->interp
, goi
->argc
, goi
->argv
, "NO PARAMS");
128 e
= jim_getopt_string(goi
, &cp
, NULL
);
132 if (strcmp(cp
, "none") == 0)
135 if (strcmp(cp
, "auto") == 0) {
136 /* Auto detect tries to look up all symbols for each RTOS,
137 * and runs the RTOS driver's _detect() function when GDB
138 * finds all symbols for any RTOS. See rtos_qsymbol(). */
139 target
->rtos_auto_detect
= true;
141 /* rtos_qsymbol() will iterate over all RTOSes. Allocate
142 * target->rtos here, and set it to the first RTOS type. */
143 return os_alloc(target
, rtos_types
[0]);
146 for (x
= 0; rtos_types
[x
]; x
++)
147 if (strcmp(cp
, rtos_types
[x
]->name
) == 0)
148 return os_alloc_create(target
, rtos_types
[x
]);
150 Jim_SetResultFormatted(goi
->interp
, "Unknown RTOS type %s, try one of: ", cp
);
151 res
= Jim_GetResult(goi
->interp
);
152 for (x
= 0; rtos_types
[x
]; x
++)
153 Jim_AppendStrings(goi
->interp
, res
, rtos_types
[x
]->name
, ", ", NULL
);
154 Jim_AppendStrings(goi
->interp
, res
, ", auto or none", NULL
);
159 void rtos_destroy(struct target
*target
)
164 int gdb_thread_packet(struct connection
*connection
, char const *packet
, int packet_size
)
166 struct target
*target
= get_target_from_connection(connection
);
168 return rtos_thread_packet(connection
, packet
, packet_size
); /* thread not
170 return target
->rtos
->gdb_thread_packet(connection
, packet
, packet_size
);
173 static struct symbol_table_elem
*find_symbol(const struct rtos
*os
, const char *symbol
)
175 struct symbol_table_elem
*s
;
177 for (s
= os
->symbols
; s
->symbol_name
; s
++)
178 if (!strcmp(s
->symbol_name
, symbol
))
184 static struct symbol_table_elem
*next_symbol(struct rtos
*os
, char *cur_symbol
, uint64_t cur_addr
)
187 os
->type
->get_symbol_list_to_lookup(&os
->symbols
);
190 return &os
->symbols
[0];
192 struct symbol_table_elem
*s
= find_symbol(os
, cur_symbol
);
196 s
->address
= cur_addr
;
201 /* rtos_qsymbol() processes and replies to all qSymbol packets from GDB.
203 * GDB sends a qSymbol:: packet (empty address, empty name) to notify
204 * that it can now answer qSymbol::hexcodedname queries, to look up symbols.
206 * If the qSymbol packet has no address that means GDB did not find the
207 * symbol, in which case auto-detect will move on to try the next RTOS.
209 * rtos_qsymbol() then calls the next_symbol() helper function, which
210 * iterates over symbol names for the current RTOS until it finds the
211 * symbol in the received GDB packet, and then returns the next entry
212 * in the list of symbols.
214 * If GDB replied about the last symbol for the RTOS and the RTOS was
215 * specified explicitly, then no further symbol lookup is done. When
216 * auto-detecting, the RTOS driver _detect() function must return success.
218 * The symbol is tried twice to handle the -flto case with gcc. The first
219 * attempt uses the symbol as-is, and the second attempt tries the symbol
220 * with ".lto_priv.0" appended to it. We only consider the first static
221 * symbol here from the -flto case. (Each subsequent static symbol with
222 * the same name is exported as .lto_priv.1, .lto_priv.2, etc.)
224 * rtos_qsymbol() returns 1 if an RTOS has been detected, or 0 otherwise.
226 int rtos_qsymbol(struct connection
*connection
, char const *packet
, int packet_size
)
228 int rtos_detected
= 0;
231 char reply
[GDB_BUFFER_SIZE
+ 1], cur_sym
[GDB_BUFFER_SIZE
/ 2 + 1] = ""; /* Extra byte for null-termination */
232 struct symbol_table_elem
*next_sym
= NULL
;
233 struct target
*target
= get_target_from_connection(connection
);
234 struct rtos
*os
= target
->rtos
;
236 reply_len
= sprintf(reply
, "OK");
241 /* Decode any symbol name in the packet*/
242 size_t len
= unhexify((uint8_t *)cur_sym
, strchr(packet
+ 8, ':') + 1, strlen(strchr(packet
+ 8, ':') + 1));
245 const char no_suffix
[] = "";
246 const char lto_suffix
[] = ".lto_priv.0";
247 const size_t lto_suffix_len
= strlen(lto_suffix
);
249 const char *cur_suffix
;
250 const char *next_suffix
;
252 /* Detect what suffix was used during the previous symbol lookup attempt, and
253 * speculatively determine the next suffix (only used for the unknown address case) */
254 if (len
> lto_suffix_len
&& !strcmp(cur_sym
+ len
- lto_suffix_len
, lto_suffix
)) {
255 /* Trim the suffix from cur_sym for comparison purposes below */
256 cur_sym
[len
- lto_suffix_len
] = '\0';
257 cur_suffix
= lto_suffix
;
260 cur_suffix
= no_suffix
;
261 next_suffix
= lto_suffix
;
264 if ((strcmp(packet
, "qSymbol::") != 0) && /* GDB is not offering symbol lookup for the first time */
265 (!sscanf(packet
, "qSymbol:%" SCNx64
":", &addr
))) { /* GDB did not find an address for a symbol */
267 /* GDB could not find an address for the previous symbol */
268 struct symbol_table_elem
*sym
= find_symbol(os
, cur_sym
);
272 } else if (sym
&& !sym
->optional
) { /* the symbol is mandatory for this RTOS */
273 if (!target
->rtos_auto_detect
) {
274 LOG_WARNING("RTOS %s not detected. (GDB could not find symbol \'%s\')", os
->type
->name
, cur_sym
);
277 /* Autodetecting RTOS - try next RTOS */
278 if (!rtos_try_next(target
)) {
279 LOG_WARNING("No RTOS could be auto-detected!");
283 /* Next RTOS selected - invalidate current symbol */
289 LOG_DEBUG("RTOS: Address of symbol '%s%s' is 0x%" PRIx64
, cur_sym
, cur_suffix
, addr
);
292 next_sym
= next_symbol(os
, cur_sym
, addr
);
293 next_suffix
= no_suffix
;
296 /* Should never happen unless the debugger misbehaves */
298 LOG_WARNING("RTOS: Debugger sent us qSymbol with '%s%s' that we did not ask for", cur_sym
, cur_suffix
);
302 if (!next_sym
->symbol_name
) {
303 /* No more symbols need looking up */
305 if (!target
->rtos_auto_detect
) {
310 if (os
->type
->detect_rtos(target
)) {
311 LOG_INFO("Auto-detected RTOS: %s", os
->type
->name
);
315 LOG_WARNING("No RTOS could be auto-detected!");
322 reply_len
= 8; /* snprintf(..., "qSymbol:") */
323 reply_len
+= 2 * strlen(next_sym
->symbol_name
); /* hexify(..., next_sym->symbol_name, ...) */
324 reply_len
+= 2 * strlen(next_suffix
); /* hexify(..., next_suffix, ...) */
325 reply_len
+= 1; /* Terminating NUL */
326 if (reply_len
> sizeof(reply
)) {
327 LOG_ERROR("ERROR: RTOS symbol '%s%s' name is too long for GDB!", next_sym
->symbol_name
, next_suffix
);
331 LOG_DEBUG("RTOS: Requesting symbol lookup of '%s%s' from the debugger", next_sym
->symbol_name
, next_suffix
);
333 reply_len
= snprintf(reply
, sizeof(reply
), "qSymbol:");
334 reply_len
+= hexify(reply
+ reply_len
,
335 (const uint8_t *)next_sym
->symbol_name
, strlen(next_sym
->symbol_name
),
336 sizeof(reply
) - reply_len
);
337 reply_len
+= hexify(reply
+ reply_len
,
338 (const uint8_t *)next_suffix
, strlen(next_suffix
),
339 sizeof(reply
) - reply_len
);
342 gdb_put_packet(connection
, reply
, reply_len
);
343 return rtos_detected
;
346 int rtos_thread_packet(struct connection
*connection
, char const *packet
, int packet_size
)
348 struct target
*target
= get_target_from_connection(connection
);
350 if (strncmp(packet
, "qThreadExtraInfo,", 17) == 0) {
351 if ((target
->rtos
) && (target
->rtos
->thread_details
) &&
352 (target
->rtos
->thread_count
!= 0)) {
353 threadid_t threadid
= 0;
355 sscanf(packet
, "qThreadExtraInfo,%" SCNx64
, &threadid
);
357 if ((target
->rtos
) && (target
->rtos
->thread_details
)) {
359 for (thread_num
= 0; thread_num
< target
->rtos
->thread_count
; thread_num
++) {
360 if (target
->rtos
->thread_details
[thread_num
].threadid
== threadid
) {
361 if (target
->rtos
->thread_details
[thread_num
].exists
)
367 gdb_put_packet(connection
, "E01", 3); /* thread not found */
371 struct thread_detail
*detail
= &target
->rtos
->thread_details
[found
];
374 if (detail
->thread_name_str
)
375 str_size
+= strlen(detail
->thread_name_str
);
376 if (detail
->extra_info_str
)
377 str_size
+= strlen(detail
->extra_info_str
);
379 char *tmp_str
= calloc(str_size
+ 9, sizeof(char));
380 char *tmp_str_ptr
= tmp_str
;
382 if (detail
->thread_name_str
)
383 tmp_str_ptr
+= sprintf(tmp_str_ptr
, "Name: %s", detail
->thread_name_str
);
384 if (detail
->extra_info_str
) {
385 if (tmp_str_ptr
!= tmp_str
)
386 tmp_str_ptr
+= sprintf(tmp_str_ptr
, ", ");
387 tmp_str_ptr
+= sprintf(tmp_str_ptr
, "%s", detail
->extra_info_str
);
390 assert(strlen(tmp_str
) ==
391 (size_t) (tmp_str_ptr
- tmp_str
));
393 char *hex_str
= malloc(strlen(tmp_str
) * 2 + 1);
394 size_t pkt_len
= hexify(hex_str
, (const uint8_t *)tmp_str
,
395 strlen(tmp_str
), strlen(tmp_str
) * 2 + 1);
397 gdb_put_packet(connection
, hex_str
, pkt_len
);
403 gdb_put_packet(connection
, "", 0);
405 } else if (strncmp(packet
, "qSymbol", 7) == 0) {
406 if (rtos_qsymbol(connection
, packet
, packet_size
) == 1) {
407 if (target
->rtos_auto_detect
== true) {
408 target
->rtos_auto_detect
= false;
409 target
->rtos
->type
->create(target
);
411 target
->rtos
->type
->update_threads(target
->rtos
);
414 } else if (strncmp(packet
, "qfThreadInfo", 12) == 0) {
417 if (target
->rtos
->thread_count
== 0) {
418 gdb_put_packet(connection
, "l", 1);
420 /*thread id are 16 char +1 for ',' */
421 char *out_str
= malloc(17 * target
->rtos
->thread_count
+ 1);
422 char *tmp_str
= out_str
;
423 for (i
= 0; i
< target
->rtos
->thread_count
; i
++) {
424 tmp_str
+= sprintf(tmp_str
, "%c%016" PRIx64
, i
== 0 ? 'm' : ',',
425 target
->rtos
->thread_details
[i
].threadid
);
427 gdb_put_packet(connection
, out_str
, strlen(out_str
));
431 gdb_put_packet(connection
, "l", 1);
434 } else if (strncmp(packet
, "qsThreadInfo", 12) == 0) {
435 gdb_put_packet(connection
, "l", 1);
437 } else if (strncmp(packet
, "qAttached", 9) == 0) {
438 gdb_put_packet(connection
, "1", 1);
440 } else if (strncmp(packet
, "qOffsets", 8) == 0) {
441 char offsets
[] = "Text=0;Data=0;Bss=0";
442 gdb_put_packet(connection
, offsets
, sizeof(offsets
)-1);
444 } else if (strncmp(packet
, "qCRC:", 5) == 0) {
445 /* make sure we check this before "qC" packet below
446 * otherwise it gets incorrectly handled */
447 return GDB_THREAD_PACKET_NOT_CONSUMED
;
448 } else if (strncmp(packet
, "qC", 2) == 0) {
452 size
= snprintf(buffer
, 19, "QC%016" PRIx64
, target
->rtos
->current_thread
);
453 gdb_put_packet(connection
, buffer
, size
);
455 gdb_put_packet(connection
, "QC0", 3);
457 } else if (packet
[0] == 'T') { /* Is thread alive? */
460 sscanf(packet
, "T%" SCNx64
, &threadid
);
461 if ((target
->rtos
) && (target
->rtos
->thread_details
)) {
463 for (thread_num
= 0; thread_num
< target
->rtos
->thread_count
; thread_num
++) {
464 if (target
->rtos
->thread_details
[thread_num
].threadid
== threadid
) {
465 if (target
->rtos
->thread_details
[thread_num
].exists
)
471 gdb_put_packet(connection
, "OK", 2); /* thread alive */
473 gdb_put_packet(connection
, "E01", 3); /* thread not found */
475 } else if (packet
[0] == 'H') { /* Set current thread ( 'c' for step and continue, 'g' for
476 * all other operations ) */
477 if ((packet
[1] == 'g') && (target
->rtos
)) {
479 sscanf(packet
, "Hg%16" SCNx64
, &threadid
);
480 LOG_DEBUG("RTOS: GDB requested to set current thread to 0x%" PRIx64
, threadid
);
481 /* threadid of 0 indicates target should choose */
483 target
->rtos
->current_threadid
= target
->rtos
->current_thread
;
485 target
->rtos
->current_threadid
= threadid
;
487 gdb_put_packet(connection
, "OK", 2);
491 return GDB_THREAD_PACKET_NOT_CONSUMED
;
494 static int rtos_put_gdb_reg_list(struct connection
*connection
,
495 struct rtos_reg
*reg_list
, int num_regs
)
497 size_t num_bytes
= 1; /* NUL */
498 for (int i
= 0; i
< num_regs
; ++i
)
499 num_bytes
+= DIV_ROUND_UP(reg_list
[i
].size
, 8) * 2;
501 char *hex
= malloc(num_bytes
);
504 for (int i
= 0; i
< num_regs
; ++i
) {
505 size_t count
= DIV_ROUND_UP(reg_list
[i
].size
, 8);
506 size_t n
= hexify(hex_p
, reg_list
[i
].value
, count
, num_bytes
);
511 gdb_put_packet(connection
, hex
, strlen(hex
));
517 /** Look through all registers to find this register. */
518 int rtos_get_gdb_reg(struct connection
*connection
, int reg_num
)
520 struct target
*target
= get_target_from_connection(connection
);
521 int64_t current_threadid
= target
->rtos
->current_threadid
;
522 if ((target
->rtos
) && (current_threadid
!= -1) &&
523 (current_threadid
!= 0) &&
524 ((current_threadid
!= target
->rtos
->current_thread
) ||
525 (target
->smp
))) { /* in smp several current thread are possible */
526 struct rtos_reg
*reg_list
;
529 LOG_DEBUG("getting register %d for thread 0x%" PRIx64
530 ", target->rtos->current_thread=0x%" PRIx64
,
533 target
->rtos
->current_thread
);
536 if (target
->rtos
->type
->get_thread_reg
) {
537 reg_list
= calloc(1, sizeof(*reg_list
));
539 retval
= target
->rtos
->type
->get_thread_reg(target
->rtos
,
540 current_threadid
, reg_num
, ®_list
[0]);
541 if (retval
!= ERROR_OK
) {
542 LOG_ERROR("RTOS: failed to get register %d", reg_num
);
546 retval
= target
->rtos
->type
->get_thread_reg_list(target
->rtos
,
550 if (retval
!= ERROR_OK
) {
551 LOG_ERROR("RTOS: failed to get register list");
556 for (int i
= 0; i
< num_regs
; ++i
) {
557 if (reg_list
[i
].number
== (uint32_t)reg_num
) {
558 rtos_put_gdb_reg_list(connection
, reg_list
+ i
, 1);
569 /** Return a list of general registers. */
570 int rtos_get_gdb_reg_list(struct connection
*connection
)
572 struct target
*target
= get_target_from_connection(connection
);
573 int64_t current_threadid
= target
->rtos
->current_threadid
;
574 if ((target
->rtos
) && (current_threadid
!= -1) &&
575 (current_threadid
!= 0) &&
576 ((current_threadid
!= target
->rtos
->current_thread
) ||
577 (target
->smp
))) { /* in smp several current thread are possible */
578 struct rtos_reg
*reg_list
;
581 LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
582 ", target->rtos->current_thread=0x%" PRIx64
"\r\n",
584 target
->rtos
->current_thread
);
586 int retval
= target
->rtos
->type
->get_thread_reg_list(target
->rtos
,
590 if (retval
!= ERROR_OK
) {
591 LOG_ERROR("RTOS: failed to get register list");
595 rtos_put_gdb_reg_list(connection
, reg_list
, num_regs
);
603 int rtos_set_reg(struct connection
*connection
, int reg_num
,
606 struct target
*target
= get_target_from_connection(connection
);
607 int64_t current_threadid
= target
->rtos
->current_threadid
;
608 if ((target
->rtos
) &&
609 (target
->rtos
->type
->set_reg
) &&
610 (current_threadid
!= -1) &&
611 (current_threadid
!= 0)) {
612 return target
->rtos
->type
->set_reg(target
->rtos
, reg_num
, reg_value
);
617 int rtos_generic_stack_read(struct target
*target
,
618 const struct rtos_register_stacking
*stacking
,
620 struct rtos_reg
**reg_list
,
625 if (stack_ptr
== 0) {
626 LOG_ERROR("Error: null stack pointer in thread");
630 uint8_t *stack_data
= malloc(stacking
->stack_registers_size
);
631 uint32_t address
= stack_ptr
;
633 if (stacking
->stack_growth_direction
== 1)
634 address
-= stacking
->stack_registers_size
;
635 retval
= target_read_buffer(target
, address
, stacking
->stack_registers_size
, stack_data
);
636 if (retval
!= ERROR_OK
) {
638 LOG_ERROR("Error reading stack frame from thread");
641 LOG_DEBUG("RTOS: Read stack frame at 0x%" PRIx32
, address
);
644 LOG_OUTPUT("Stack Data :");
645 for (i
= 0; i
< stacking
->stack_registers_size
; i
++)
646 LOG_OUTPUT("%02X", stack_data
[i
]);
650 target_addr_t new_stack_ptr
;
651 if (stacking
->calculate_process_stack
) {
652 new_stack_ptr
= stacking
->calculate_process_stack(target
,
653 stack_data
, stacking
, stack_ptr
);
655 new_stack_ptr
= stack_ptr
- stacking
->stack_growth_direction
*
656 stacking
->stack_registers_size
;
659 *reg_list
= calloc(stacking
->num_output_registers
, sizeof(struct rtos_reg
));
660 *num_regs
= stacking
->num_output_registers
;
662 for (int i
= 0; i
< stacking
->num_output_registers
; ++i
) {
663 (*reg_list
)[i
].number
= stacking
->register_offsets
[i
].number
;
664 (*reg_list
)[i
].size
= stacking
->register_offsets
[i
].width_bits
;
666 int offset
= stacking
->register_offsets
[i
].offset
;
668 buf_cpy(&new_stack_ptr
, (*reg_list
)[i
].value
, (*reg_list
)[i
].size
);
669 else if (offset
!= -1)
670 buf_cpy(stack_data
+ offset
, (*reg_list
)[i
].value
, (*reg_list
)[i
].size
);
674 /* LOG_OUTPUT("Output register string: %s\r\n", *hex_reg_list); */
678 static int rtos_try_next(struct target
*target
)
680 struct rtos
*os
= target
->rtos
;
681 struct rtos_type
**type
= rtos_types
;
686 while (*type
&& os
->type
!= *type
)
689 if (!*type
|| !*(++type
))
700 int rtos_update_threads(struct target
*target
)
702 if ((target
->rtos
) && (target
->rtos
->type
))
703 target
->rtos
->type
->update_threads(target
->rtos
);
707 void rtos_free_threadlist(struct rtos
*rtos
)
709 if (rtos
->thread_details
) {
712 for (j
= 0; j
< rtos
->thread_count
; j
++) {
713 struct thread_detail
*current_thread
= &rtos
->thread_details
[j
];
714 free(current_thread
->thread_name_str
);
715 free(current_thread
->extra_info_str
);
717 free(rtos
->thread_details
);
718 rtos
->thread_details
= NULL
;
719 rtos
->thread_count
= 0;
720 rtos
->current_threadid
= -1;
721 rtos
->current_thread
= 0;
725 int rtos_read_buffer(struct target
*target
, target_addr_t address
,
726 uint32_t size
, uint8_t *buffer
)
728 if (target
->rtos
->type
->read_buffer
)
729 return target
->rtos
->type
->read_buffer(target
->rtos
, address
, size
, buffer
);
730 return ERROR_NOT_IMPLEMENTED
;
733 int rtos_write_buffer(struct target
*target
, target_addr_t address
,
734 uint32_t size
, const uint8_t *buffer
)
736 if (target
->rtos
->type
->write_buffer
)
737 return target
->rtos
->type
->write_buffer(target
->rtos
, address
, size
, buffer
);
738 return ERROR_NOT_IMPLEMENTED
;
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)