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
;
32 extern struct rtos_type rtkernel_rtos
;
34 static struct rtos_type
*rtos_types
[] = {
48 /* keep this as last, as it always matches with rtos auto */
53 static int rtos_try_next(struct target
*target
);
55 int rtos_thread_packet(struct connection
*connection
, const char *packet
, int packet_size
);
57 int rtos_smp_init(struct target
*target
)
59 if (target
->rtos
->type
->smp_init
)
60 return target
->rtos
->type
->smp_init(target
);
61 return ERROR_TARGET_INIT_FAILED
;
64 static int rtos_target_for_threadid(struct connection
*connection
, int64_t threadid
, struct target
**t
)
66 struct target
*curr
= get_target_from_connection(connection
);
73 static int os_alloc(struct target
*target
, struct rtos_type
*ostype
)
75 struct rtos
*os
= target
->rtos
= calloc(1, sizeof(struct rtos
));
81 os
->current_threadid
= -1;
82 os
->current_thread
= 0;
86 /* RTOS drivers can override the packet handler in _create(). */
87 os
->gdb_thread_packet
= rtos_thread_packet
;
88 os
->gdb_target_for_threadid
= rtos_target_for_threadid
;
93 static void os_free(struct target
*target
)
98 free(target
->rtos
->symbols
);
103 static int os_alloc_create(struct target
*target
, struct rtos_type
*ostype
)
105 int ret
= os_alloc(target
, ostype
);
108 ret
= target
->rtos
->type
->create(target
);
116 int rtos_create(struct jim_getopt_info
*goi
, struct target
*target
)
123 if (!goi
->isconfigure
&& goi
->argc
!= 0) {
124 Jim_WrongNumArgs(goi
->interp
, goi
->argc
, goi
->argv
, "NO PARAMS");
130 e
= jim_getopt_string(goi
, &cp
, NULL
);
134 if (strcmp(cp
, "none") == 0)
137 if (strcmp(cp
, "auto") == 0) {
138 /* Auto detect tries to look up all symbols for each RTOS,
139 * and runs the RTOS driver's _detect() function when GDB
140 * finds all symbols for any RTOS. See rtos_qsymbol(). */
141 target
->rtos_auto_detect
= true;
143 /* rtos_qsymbol() will iterate over all RTOSes. Allocate
144 * target->rtos here, and set it to the first RTOS type. */
145 return os_alloc(target
, rtos_types
[0]);
148 for (x
= 0; rtos_types
[x
]; x
++)
149 if (strcmp(cp
, rtos_types
[x
]->name
) == 0)
150 return os_alloc_create(target
, rtos_types
[x
]);
152 Jim_SetResultFormatted(goi
->interp
, "Unknown RTOS type %s, try one of: ", cp
);
153 res
= Jim_GetResult(goi
->interp
);
154 for (x
= 0; rtos_types
[x
]; x
++)
155 Jim_AppendStrings(goi
->interp
, res
, rtos_types
[x
]->name
, ", ", NULL
);
156 Jim_AppendStrings(goi
->interp
, res
, ", auto or none", NULL
);
161 void rtos_destroy(struct target
*target
)
166 int gdb_thread_packet(struct connection
*connection
, char const *packet
, int packet_size
)
168 struct target
*target
= get_target_from_connection(connection
);
170 return rtos_thread_packet(connection
, packet
, packet_size
); /* thread not
172 return target
->rtos
->gdb_thread_packet(connection
, packet
, packet_size
);
175 static struct symbol_table_elem
*find_symbol(const struct rtos
*os
, const char *symbol
)
177 struct symbol_table_elem
*s
;
179 for (s
= os
->symbols
; s
->symbol_name
; s
++)
180 if (!strcmp(s
->symbol_name
, symbol
))
186 static struct symbol_table_elem
*next_symbol(struct rtos
*os
, char *cur_symbol
, uint64_t cur_addr
)
189 os
->type
->get_symbol_list_to_lookup(&os
->symbols
);
192 return &os
->symbols
[0];
194 struct symbol_table_elem
*s
= find_symbol(os
, cur_symbol
);
198 s
->address
= cur_addr
;
203 /* rtos_qsymbol() processes and replies to all qSymbol packets from GDB.
205 * GDB sends a qSymbol:: packet (empty address, empty name) to notify
206 * that it can now answer qSymbol::hexcodedname queries, to look up symbols.
208 * If the qSymbol packet has no address that means GDB did not find the
209 * symbol, in which case auto-detect will move on to try the next RTOS.
211 * rtos_qsymbol() then calls the next_symbol() helper function, which
212 * iterates over symbol names for the current RTOS until it finds the
213 * symbol in the received GDB packet, and then returns the next entry
214 * in the list of symbols.
216 * If GDB replied about the last symbol for the RTOS and the RTOS was
217 * specified explicitly, then no further symbol lookup is done. When
218 * auto-detecting, the RTOS driver _detect() function must return success.
220 * The symbol is tried twice to handle the -flto case with gcc. The first
221 * attempt uses the symbol as-is, and the second attempt tries the symbol
222 * with ".lto_priv.0" appended to it. We only consider the first static
223 * symbol here from the -flto case. (Each subsequent static symbol with
224 * the same name is exported as .lto_priv.1, .lto_priv.2, etc.)
226 * rtos_qsymbol() returns 1 if an RTOS has been detected, or 0 otherwise.
228 int rtos_qsymbol(struct connection
*connection
, char const *packet
, int packet_size
)
230 int rtos_detected
= 0;
233 char reply
[GDB_BUFFER_SIZE
+ 1], cur_sym
[GDB_BUFFER_SIZE
/ 2 + 1] = ""; /* Extra byte for null-termination */
234 struct symbol_table_elem
*next_sym
= NULL
;
235 struct target
*target
= get_target_from_connection(connection
);
236 struct rtos
*os
= target
->rtos
;
238 reply_len
= sprintf(reply
, "OK");
243 /* Decode any symbol name in the packet*/
244 size_t len
= unhexify((uint8_t *)cur_sym
, strchr(packet
+ 8, ':') + 1, strlen(strchr(packet
+ 8, ':') + 1));
247 const char no_suffix
[] = "";
248 const char lto_suffix
[] = ".lto_priv.0";
249 const size_t lto_suffix_len
= strlen(lto_suffix
);
251 const char *cur_suffix
;
252 const char *next_suffix
;
254 /* Detect what suffix was used during the previous symbol lookup attempt, and
255 * speculatively determine the next suffix (only used for the unknown address case) */
256 if (len
> lto_suffix_len
&& !strcmp(cur_sym
+ len
- lto_suffix_len
, lto_suffix
)) {
257 /* Trim the suffix from cur_sym for comparison purposes below */
258 cur_sym
[len
- lto_suffix_len
] = '\0';
259 cur_suffix
= lto_suffix
;
262 cur_suffix
= no_suffix
;
263 next_suffix
= lto_suffix
;
266 if ((strcmp(packet
, "qSymbol::") != 0) && /* GDB is not offering symbol lookup for the first time */
267 (!sscanf(packet
, "qSymbol:%" SCNx64
":", &addr
))) { /* GDB did not find an address for a symbol */
269 /* GDB could not find an address for the previous symbol */
270 struct symbol_table_elem
*sym
= find_symbol(os
, cur_sym
);
274 } else if (sym
&& !sym
->optional
) { /* the symbol is mandatory for this RTOS */
275 if (!target
->rtos_auto_detect
) {
276 LOG_WARNING("RTOS %s not detected. (GDB could not find symbol \'%s\')", os
->type
->name
, cur_sym
);
279 /* Autodetecting RTOS - try next RTOS */
280 if (!rtos_try_next(target
)) {
281 LOG_WARNING("No RTOS could be auto-detected!");
285 /* Next RTOS selected - invalidate current symbol */
291 LOG_DEBUG("RTOS: Address of symbol '%s%s' is 0x%" PRIx64
, cur_sym
, cur_suffix
, addr
);
294 next_sym
= next_symbol(os
, cur_sym
, addr
);
295 next_suffix
= no_suffix
;
298 /* Should never happen unless the debugger misbehaves */
300 LOG_WARNING("RTOS: Debugger sent us qSymbol with '%s%s' that we did not ask for", cur_sym
, cur_suffix
);
304 if (!next_sym
->symbol_name
) {
305 /* No more symbols need looking up */
307 if (!target
->rtos_auto_detect
) {
312 if (os
->type
->detect_rtos(target
)) {
313 LOG_INFO("Auto-detected RTOS: %s", os
->type
->name
);
317 LOG_WARNING("No RTOS could be auto-detected!");
324 reply_len
= 8; /* snprintf(..., "qSymbol:") */
325 reply_len
+= 2 * strlen(next_sym
->symbol_name
); /* hexify(..., next_sym->symbol_name, ...) */
326 reply_len
+= 2 * strlen(next_suffix
); /* hexify(..., next_suffix, ...) */
327 reply_len
+= 1; /* Terminating NUL */
328 if (reply_len
> sizeof(reply
)) {
329 LOG_ERROR("ERROR: RTOS symbol '%s%s' name is too long for GDB!", next_sym
->symbol_name
, next_suffix
);
333 LOG_DEBUG("RTOS: Requesting symbol lookup of '%s%s' from the debugger", next_sym
->symbol_name
, next_suffix
);
335 reply_len
= snprintf(reply
, sizeof(reply
), "qSymbol:");
336 reply_len
+= hexify(reply
+ reply_len
,
337 (const uint8_t *)next_sym
->symbol_name
, strlen(next_sym
->symbol_name
),
338 sizeof(reply
) - reply_len
);
339 reply_len
+= hexify(reply
+ reply_len
,
340 (const uint8_t *)next_suffix
, strlen(next_suffix
),
341 sizeof(reply
) - reply_len
);
344 gdb_put_packet(connection
, reply
, reply_len
);
345 return rtos_detected
;
348 int rtos_thread_packet(struct connection
*connection
, char const *packet
, int packet_size
)
350 struct target
*target
= get_target_from_connection(connection
);
352 if (strncmp(packet
, "qThreadExtraInfo,", 17) == 0) {
353 if ((target
->rtos
) && (target
->rtos
->thread_details
) &&
354 (target
->rtos
->thread_count
!= 0)) {
355 threadid_t threadid
= 0;
357 sscanf(packet
, "qThreadExtraInfo,%" SCNx64
, &threadid
);
359 if ((target
->rtos
) && (target
->rtos
->thread_details
)) {
361 for (thread_num
= 0; thread_num
< target
->rtos
->thread_count
; thread_num
++) {
362 if (target
->rtos
->thread_details
[thread_num
].threadid
== threadid
) {
363 if (target
->rtos
->thread_details
[thread_num
].exists
)
369 gdb_put_packet(connection
, "E01", 3); /* thread not found */
373 struct thread_detail
*detail
= &target
->rtos
->thread_details
[found
];
376 if (detail
->thread_name_str
)
377 str_size
+= strlen(detail
->thread_name_str
);
378 if (detail
->extra_info_str
)
379 str_size
+= strlen(detail
->extra_info_str
);
381 char *tmp_str
= calloc(str_size
+ 9, sizeof(char));
382 char *tmp_str_ptr
= tmp_str
;
384 if (detail
->thread_name_str
)
385 tmp_str_ptr
+= sprintf(tmp_str_ptr
, "Name: %s", detail
->thread_name_str
);
386 if (detail
->extra_info_str
) {
387 if (tmp_str_ptr
!= tmp_str
)
388 tmp_str_ptr
+= sprintf(tmp_str_ptr
, ", ");
389 tmp_str_ptr
+= sprintf(tmp_str_ptr
, "%s", detail
->extra_info_str
);
392 assert(strlen(tmp_str
) ==
393 (size_t) (tmp_str_ptr
- tmp_str
));
395 char *hex_str
= malloc(strlen(tmp_str
) * 2 + 1);
396 size_t pkt_len
= hexify(hex_str
, (const uint8_t *)tmp_str
,
397 strlen(tmp_str
), strlen(tmp_str
) * 2 + 1);
399 gdb_put_packet(connection
, hex_str
, pkt_len
);
405 gdb_put_packet(connection
, "", 0);
407 } else if (strncmp(packet
, "qSymbol", 7) == 0) {
408 if (rtos_qsymbol(connection
, packet
, packet_size
) == 1) {
409 if (target
->rtos_auto_detect
== true) {
410 target
->rtos_auto_detect
= false;
411 target
->rtos
->type
->create(target
);
413 target
->rtos
->type
->update_threads(target
->rtos
);
416 } else if (strncmp(packet
, "qfThreadInfo", 12) == 0) {
419 if (target
->rtos
->thread_count
== 0) {
420 gdb_put_packet(connection
, "l", 1);
422 /*thread id are 16 char +1 for ',' */
423 char *out_str
= malloc(17 * target
->rtos
->thread_count
+ 1);
424 char *tmp_str
= out_str
;
425 for (i
= 0; i
< target
->rtos
->thread_count
; i
++) {
426 tmp_str
+= sprintf(tmp_str
, "%c%016" PRIx64
, i
== 0 ? 'm' : ',',
427 target
->rtos
->thread_details
[i
].threadid
);
429 gdb_put_packet(connection
, out_str
, strlen(out_str
));
433 gdb_put_packet(connection
, "l", 1);
436 } else if (strncmp(packet
, "qsThreadInfo", 12) == 0) {
437 gdb_put_packet(connection
, "l", 1);
439 } else if (strncmp(packet
, "qAttached", 9) == 0) {
440 gdb_put_packet(connection
, "1", 1);
442 } else if (strncmp(packet
, "qOffsets", 8) == 0) {
443 char offsets
[] = "Text=0;Data=0;Bss=0";
444 gdb_put_packet(connection
, offsets
, sizeof(offsets
)-1);
446 } else if (strncmp(packet
, "qCRC:", 5) == 0) {
447 /* make sure we check this before "qC" packet below
448 * otherwise it gets incorrectly handled */
449 return GDB_THREAD_PACKET_NOT_CONSUMED
;
450 } else if (strncmp(packet
, "qC", 2) == 0) {
454 size
= snprintf(buffer
, 19, "QC%016" PRIx64
, target
->rtos
->current_thread
);
455 gdb_put_packet(connection
, buffer
, size
);
457 gdb_put_packet(connection
, "QC0", 3);
459 } else if (packet
[0] == 'T') { /* Is thread alive? */
462 sscanf(packet
, "T%" SCNx64
, &threadid
);
463 if ((target
->rtos
) && (target
->rtos
->thread_details
)) {
465 for (thread_num
= 0; thread_num
< target
->rtos
->thread_count
; thread_num
++) {
466 if (target
->rtos
->thread_details
[thread_num
].threadid
== threadid
) {
467 if (target
->rtos
->thread_details
[thread_num
].exists
)
473 gdb_put_packet(connection
, "OK", 2); /* thread alive */
475 gdb_put_packet(connection
, "E01", 3); /* thread not found */
477 } else if (packet
[0] == 'H') { /* Set current thread ( 'c' for step and continue, 'g' for
478 * all other operations ) */
479 if ((packet
[1] == 'g') && (target
->rtos
)) {
481 sscanf(packet
, "Hg%16" SCNx64
, &threadid
);
482 LOG_DEBUG("RTOS: GDB requested to set current thread to 0x%" PRIx64
, threadid
);
483 /* threadid of 0 indicates target should choose */
485 target
->rtos
->current_threadid
= target
->rtos
->current_thread
;
487 target
->rtos
->current_threadid
= threadid
;
489 gdb_put_packet(connection
, "OK", 2);
493 return GDB_THREAD_PACKET_NOT_CONSUMED
;
496 static int rtos_put_gdb_reg_list(struct connection
*connection
,
497 struct rtos_reg
*reg_list
, int num_regs
)
499 size_t num_bytes
= 1; /* NUL */
500 for (int i
= 0; i
< num_regs
; ++i
)
501 num_bytes
+= DIV_ROUND_UP(reg_list
[i
].size
, 8) * 2;
503 char *hex
= malloc(num_bytes
);
506 for (int i
= 0; i
< num_regs
; ++i
) {
507 size_t count
= DIV_ROUND_UP(reg_list
[i
].size
, 8);
508 size_t n
= hexify(hex_p
, reg_list
[i
].value
, count
, num_bytes
);
513 gdb_put_packet(connection
, hex
, strlen(hex
));
519 /** Look through all registers to find this register. */
520 int rtos_get_gdb_reg(struct connection
*connection
, int reg_num
)
522 struct target
*target
= get_target_from_connection(connection
);
523 int64_t current_threadid
= target
->rtos
->current_threadid
;
524 if ((target
->rtos
) && (current_threadid
!= -1) &&
525 (current_threadid
!= 0) &&
526 ((current_threadid
!= target
->rtos
->current_thread
) ||
527 (target
->smp
))) { /* in smp several current thread are possible */
528 struct rtos_reg
*reg_list
;
531 LOG_DEBUG("getting register %d for thread 0x%" PRIx64
532 ", target->rtos->current_thread=0x%" PRIx64
,
535 target
->rtos
->current_thread
);
538 if (target
->rtos
->type
->get_thread_reg
) {
539 reg_list
= calloc(1, sizeof(*reg_list
));
541 retval
= target
->rtos
->type
->get_thread_reg(target
->rtos
,
542 current_threadid
, reg_num
, ®_list
[0]);
543 if (retval
!= ERROR_OK
) {
544 LOG_ERROR("RTOS: failed to get register %d", reg_num
);
548 retval
= target
->rtos
->type
->get_thread_reg_list(target
->rtos
,
552 if (retval
!= ERROR_OK
) {
553 LOG_ERROR("RTOS: failed to get register list");
558 for (int i
= 0; i
< num_regs
; ++i
) {
559 if (reg_list
[i
].number
== (uint32_t)reg_num
) {
560 rtos_put_gdb_reg_list(connection
, reg_list
+ i
, 1);
571 /** Return a list of general registers. */
572 int rtos_get_gdb_reg_list(struct connection
*connection
)
574 struct target
*target
= get_target_from_connection(connection
);
575 int64_t current_threadid
= target
->rtos
->current_threadid
;
576 if ((target
->rtos
) && (current_threadid
!= -1) &&
577 (current_threadid
!= 0) &&
578 ((current_threadid
!= target
->rtos
->current_thread
) ||
579 (target
->smp
))) { /* in smp several current thread are possible */
580 struct rtos_reg
*reg_list
;
583 LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
584 ", target->rtos->current_thread=0x%" PRIx64
"\r\n",
586 target
->rtos
->current_thread
);
588 int retval
= target
->rtos
->type
->get_thread_reg_list(target
->rtos
,
592 if (retval
!= ERROR_OK
) {
593 LOG_ERROR("RTOS: failed to get register list");
597 rtos_put_gdb_reg_list(connection
, reg_list
, num_regs
);
605 int rtos_set_reg(struct connection
*connection
, int reg_num
,
608 struct target
*target
= get_target_from_connection(connection
);
609 int64_t current_threadid
= target
->rtos
->current_threadid
;
610 if ((target
->rtos
) &&
611 (target
->rtos
->type
->set_reg
) &&
612 (current_threadid
!= -1) &&
613 (current_threadid
!= 0)) {
614 return target
->rtos
->type
->set_reg(target
->rtos
, reg_num
, reg_value
);
619 int rtos_generic_stack_read(struct target
*target
,
620 const struct rtos_register_stacking
*stacking
,
622 struct rtos_reg
**reg_list
,
627 if (stack_ptr
== 0) {
628 LOG_ERROR("Error: null stack pointer in thread");
632 uint8_t *stack_data
= malloc(stacking
->stack_registers_size
);
633 uint32_t address
= stack_ptr
;
635 if (stacking
->stack_growth_direction
== 1)
636 address
-= stacking
->stack_registers_size
;
637 if (stacking
->read_stack
)
638 retval
= stacking
->read_stack(target
, address
, stacking
, stack_data
);
640 retval
= target_read_buffer(target
, address
, stacking
->stack_registers_size
, stack_data
);
641 if (retval
!= ERROR_OK
) {
643 LOG_ERROR("Error reading stack frame from thread");
646 LOG_DEBUG("RTOS: Read stack frame at 0x%" PRIx32
, address
);
649 LOG_OUTPUT("Stack Data :");
650 for (i
= 0; i
< stacking
->stack_registers_size
; i
++)
651 LOG_OUTPUT("%02X", stack_data
[i
]);
655 target_addr_t new_stack_ptr
;
656 if (stacking
->calculate_process_stack
) {
657 new_stack_ptr
= stacking
->calculate_process_stack(target
,
658 stack_data
, stacking
, stack_ptr
);
660 new_stack_ptr
= stack_ptr
- stacking
->stack_growth_direction
*
661 stacking
->stack_registers_size
;
664 *reg_list
= calloc(stacking
->num_output_registers
, sizeof(struct rtos_reg
));
665 *num_regs
= stacking
->num_output_registers
;
667 for (int i
= 0; i
< stacking
->num_output_registers
; ++i
) {
668 (*reg_list
)[i
].number
= stacking
->register_offsets
[i
].number
;
669 (*reg_list
)[i
].size
= stacking
->register_offsets
[i
].width_bits
;
671 int offset
= stacking
->register_offsets
[i
].offset
;
673 buf_cpy(&new_stack_ptr
, (*reg_list
)[i
].value
, (*reg_list
)[i
].size
);
674 else if (offset
!= -1)
675 buf_cpy(stack_data
+ offset
, (*reg_list
)[i
].value
, (*reg_list
)[i
].size
);
679 /* LOG_OUTPUT("Output register string: %s\r\n", *hex_reg_list); */
683 static int rtos_try_next(struct target
*target
)
685 struct rtos
*os
= target
->rtos
;
686 struct rtos_type
**type
= rtos_types
;
691 while (*type
&& os
->type
!= *type
)
694 if (!*type
|| !*(++type
))
705 int rtos_update_threads(struct target
*target
)
707 if ((target
->rtos
) && (target
->rtos
->type
))
708 target
->rtos
->type
->update_threads(target
->rtos
);
712 void rtos_free_threadlist(struct rtos
*rtos
)
714 if (rtos
->thread_details
) {
717 for (j
= 0; j
< rtos
->thread_count
; j
++) {
718 struct thread_detail
*current_thread
= &rtos
->thread_details
[j
];
719 free(current_thread
->thread_name_str
);
720 free(current_thread
->extra_info_str
);
722 free(rtos
->thread_details
);
723 rtos
->thread_details
= NULL
;
724 rtos
->thread_count
= 0;
725 rtos
->current_threadid
= -1;
726 rtos
->current_thread
= 0;
730 int rtos_read_buffer(struct target
*target
, target_addr_t address
,
731 uint32_t size
, uint8_t *buffer
)
733 if (target
->rtos
->type
->read_buffer
)
734 return target
->rtos
->type
->read_buffer(target
->rtos
, address
, size
, buffer
);
735 return ERROR_NOT_IMPLEMENTED
;
738 int rtos_write_buffer(struct target
*target
, target_addr_t address
,
739 uint32_t size
, const uint8_t *buffer
)
741 if (target
->rtos
->type
->write_buffer
)
742 return target
->rtos
->type
->write_buffer(target
->rtos
, address
, size
, buffer
);
743 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)