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"
18 static const struct rtos_type
*rtos_types
[] = {
32 /* keep this as last, as it always matches with rtos auto */
37 static int rtos_try_next(struct target
*target
);
39 int rtos_smp_init(struct target
*target
)
41 if (target
->rtos
->type
->smp_init
)
42 return target
->rtos
->type
->smp_init(target
);
43 return ERROR_TARGET_INIT_FAILED
;
46 static int rtos_target_for_threadid(struct connection
*connection
, int64_t threadid
, struct target
**t
)
48 struct target
*curr
= get_target_from_connection(connection
);
55 static int os_alloc(struct target
*target
, const struct rtos_type
*ostype
)
57 struct rtos
*os
= target
->rtos
= calloc(1, sizeof(struct rtos
));
63 os
->current_threadid
= -1;
64 os
->current_thread
= 0;
68 /* RTOS drivers can override the packet handler in _create(). */
69 os
->gdb_thread_packet
= rtos_thread_packet
;
70 os
->gdb_target_for_threadid
= rtos_target_for_threadid
;
75 static void os_free(struct target
*target
)
80 free(target
->rtos
->symbols
);
85 static int os_alloc_create(struct target
*target
, const struct rtos_type
*ostype
)
87 int ret
= os_alloc(target
, ostype
);
90 ret
= target
->rtos
->type
->create(target
);
98 int rtos_create(struct jim_getopt_info
*goi
, struct target
*target
)
105 if (!goi
->isconfigure
&& goi
->argc
!= 0) {
106 Jim_WrongNumArgs(goi
->interp
, goi
->argc
, goi
->argv
, "NO PARAMS");
112 e
= jim_getopt_string(goi
, &cp
, NULL
);
116 if (strcmp(cp
, "none") == 0)
119 if (strcmp(cp
, "auto") == 0) {
120 /* Auto detect tries to look up all symbols for each RTOS,
121 * and runs the RTOS driver's _detect() function when GDB
122 * finds all symbols for any RTOS. See rtos_qsymbol(). */
123 target
->rtos_auto_detect
= true;
125 /* rtos_qsymbol() will iterate over all RTOSes. Allocate
126 * target->rtos here, and set it to the first RTOS type. */
127 return os_alloc(target
, rtos_types
[0]);
130 for (x
= 0; rtos_types
[x
]; x
++)
131 if (strcmp(cp
, rtos_types
[x
]->name
) == 0)
132 return os_alloc_create(target
, rtos_types
[x
]);
134 Jim_SetResultFormatted(goi
->interp
, "Unknown RTOS type %s, try one of: ", cp
);
135 res
= Jim_GetResult(goi
->interp
);
136 for (x
= 0; rtos_types
[x
]; x
++)
137 Jim_AppendStrings(goi
->interp
, res
, rtos_types
[x
]->name
, ", ", NULL
);
138 Jim_AppendStrings(goi
->interp
, res
, ", auto or none", NULL
);
143 void rtos_destroy(struct target
*target
)
148 int gdb_thread_packet(struct connection
*connection
, char const *packet
, int packet_size
)
150 struct target
*target
= get_target_from_connection(connection
);
152 return rtos_thread_packet(connection
, packet
, packet_size
); /* thread not
154 return target
->rtos
->gdb_thread_packet(connection
, packet
, packet_size
);
157 static struct symbol_table_elem
*find_symbol(const struct rtos
*os
, const char *symbol
)
159 struct symbol_table_elem
*s
;
161 for (s
= os
->symbols
; s
->symbol_name
; s
++)
162 if (!strcmp(s
->symbol_name
, symbol
))
168 static struct symbol_table_elem
*next_symbol(struct rtos
*os
, char *cur_symbol
, uint64_t cur_addr
)
171 os
->type
->get_symbol_list_to_lookup(&os
->symbols
);
174 return &os
->symbols
[0];
176 struct symbol_table_elem
*s
= find_symbol(os
, cur_symbol
);
180 s
->address
= cur_addr
;
185 /* rtos_qsymbol() processes and replies to all qSymbol packets from GDB.
187 * GDB sends a qSymbol:: packet (empty address, empty name) to notify
188 * that it can now answer qSymbol::hexcodedname queries, to look up symbols.
190 * If the qSymbol packet has no address that means GDB did not find the
191 * symbol, in which case auto-detect will move on to try the next RTOS.
193 * rtos_qsymbol() then calls the next_symbol() helper function, which
194 * iterates over symbol names for the current RTOS until it finds the
195 * symbol in the received GDB packet, and then returns the next entry
196 * in the list of symbols.
198 * If GDB replied about the last symbol for the RTOS and the RTOS was
199 * specified explicitly, then no further symbol lookup is done. When
200 * auto-detecting, the RTOS driver _detect() function must return success.
202 * The symbol is tried twice to handle the -flto case with gcc. The first
203 * attempt uses the symbol as-is, and the second attempt tries the symbol
204 * with ".lto_priv.0" appended to it. We only consider the first static
205 * symbol here from the -flto case. (Each subsequent static symbol with
206 * the same name is exported as .lto_priv.1, .lto_priv.2, etc.)
208 * rtos_qsymbol() returns 1 if an RTOS has been detected, or 0 otherwise.
210 int rtos_qsymbol(struct connection
*connection
, char const *packet
, int packet_size
)
212 int rtos_detected
= 0;
215 char reply
[GDB_BUFFER_SIZE
+ 1], cur_sym
[GDB_BUFFER_SIZE
/ 2 + 1] = ""; /* Extra byte for null-termination */
216 struct symbol_table_elem
*next_sym
= NULL
;
217 struct target
*target
= get_target_from_connection(connection
);
218 struct rtos
*os
= target
->rtos
;
220 reply_len
= sprintf(reply
, "OK");
225 /* Decode any symbol name in the packet*/
226 size_t len
= unhexify((uint8_t *)cur_sym
, strchr(packet
+ 8, ':') + 1, strlen(strchr(packet
+ 8, ':') + 1));
229 const char no_suffix
[] = "";
230 const char lto_suffix
[] = ".lto_priv.0";
231 const size_t lto_suffix_len
= strlen(lto_suffix
);
233 const char *cur_suffix
;
234 const char *next_suffix
;
236 /* Detect what suffix was used during the previous symbol lookup attempt, and
237 * speculatively determine the next suffix (only used for the unknown address case) */
238 if (len
> lto_suffix_len
&& !strcmp(cur_sym
+ len
- lto_suffix_len
, lto_suffix
)) {
239 /* Trim the suffix from cur_sym for comparison purposes below */
240 cur_sym
[len
- lto_suffix_len
] = '\0';
241 cur_suffix
= lto_suffix
;
244 cur_suffix
= no_suffix
;
245 next_suffix
= lto_suffix
;
248 if ((strcmp(packet
, "qSymbol::") != 0) && /* GDB is not offering symbol lookup for the first time */
249 (!sscanf(packet
, "qSymbol:%" SCNx64
":", &addr
))) { /* GDB did not find an address for a symbol */
251 /* GDB could not find an address for the previous symbol */
252 struct symbol_table_elem
*sym
= find_symbol(os
, cur_sym
);
256 } else if (sym
&& !sym
->optional
) { /* the symbol is mandatory for this RTOS */
257 if (!target
->rtos_auto_detect
) {
258 LOG_WARNING("RTOS %s not detected. (GDB could not find symbol \'%s\')", os
->type
->name
, cur_sym
);
261 /* Autodetecting RTOS - try next RTOS */
262 if (!rtos_try_next(target
)) {
263 LOG_WARNING("No RTOS could be auto-detected!");
267 /* Next RTOS selected - invalidate current symbol */
273 LOG_DEBUG("RTOS: Address of symbol '%s%s' is 0x%" PRIx64
, cur_sym
, cur_suffix
, addr
);
276 next_sym
= next_symbol(os
, cur_sym
, addr
);
277 next_suffix
= no_suffix
;
280 /* Should never happen unless the debugger misbehaves */
282 LOG_WARNING("RTOS: Debugger sent us qSymbol with '%s%s' that we did not ask for", cur_sym
, cur_suffix
);
286 if (!next_sym
->symbol_name
) {
287 /* No more symbols need looking up */
289 if (!target
->rtos_auto_detect
) {
294 if (os
->type
->detect_rtos(target
)) {
295 LOG_INFO("Auto-detected RTOS: %s", os
->type
->name
);
299 LOG_WARNING("No RTOS could be auto-detected!");
306 reply_len
= 8; /* snprintf(..., "qSymbol:") */
307 reply_len
+= 2 * strlen(next_sym
->symbol_name
); /* hexify(..., next_sym->symbol_name, ...) */
308 reply_len
+= 2 * strlen(next_suffix
); /* hexify(..., next_suffix, ...) */
309 reply_len
+= 1; /* Terminating NUL */
310 if (reply_len
> sizeof(reply
)) {
311 LOG_ERROR("ERROR: RTOS symbol '%s%s' name is too long for GDB!", next_sym
->symbol_name
, next_suffix
);
315 LOG_DEBUG("RTOS: Requesting symbol lookup of '%s%s' from the debugger", next_sym
->symbol_name
, next_suffix
);
317 reply_len
= snprintf(reply
, sizeof(reply
), "qSymbol:");
318 reply_len
+= hexify(reply
+ reply_len
,
319 (const uint8_t *)next_sym
->symbol_name
, strlen(next_sym
->symbol_name
),
320 sizeof(reply
) - reply_len
);
321 reply_len
+= hexify(reply
+ reply_len
,
322 (const uint8_t *)next_suffix
, strlen(next_suffix
),
323 sizeof(reply
) - reply_len
);
326 gdb_put_packet(connection
, reply
, reply_len
);
327 return rtos_detected
;
330 int rtos_thread_packet(struct connection
*connection
, char const *packet
, int packet_size
)
332 struct target
*target
= get_target_from_connection(connection
);
334 if (strncmp(packet
, "qThreadExtraInfo,", 17) == 0) {
335 if ((target
->rtos
) && (target
->rtos
->thread_details
) &&
336 (target
->rtos
->thread_count
!= 0)) {
337 threadid_t threadid
= 0;
339 sscanf(packet
, "qThreadExtraInfo,%" SCNx64
, &threadid
);
341 if ((target
->rtos
) && (target
->rtos
->thread_details
)) {
343 for (thread_num
= 0; thread_num
< target
->rtos
->thread_count
; thread_num
++) {
344 if (target
->rtos
->thread_details
[thread_num
].threadid
== threadid
) {
345 if (target
->rtos
->thread_details
[thread_num
].exists
)
351 gdb_put_packet(connection
, "E01", 3); /* thread not found */
355 struct thread_detail
*detail
= &target
->rtos
->thread_details
[found
];
358 if (detail
->thread_name_str
)
359 str_size
+= strlen(detail
->thread_name_str
);
360 if (detail
->extra_info_str
)
361 str_size
+= strlen(detail
->extra_info_str
);
363 char *tmp_str
= calloc(str_size
+ 9, sizeof(char));
364 char *tmp_str_ptr
= tmp_str
;
366 if (detail
->thread_name_str
)
367 tmp_str_ptr
+= sprintf(tmp_str_ptr
, "Name: %s", detail
->thread_name_str
);
368 if (detail
->extra_info_str
) {
369 if (tmp_str_ptr
!= tmp_str
)
370 tmp_str_ptr
+= sprintf(tmp_str_ptr
, ", ");
371 tmp_str_ptr
+= sprintf(tmp_str_ptr
, "%s", detail
->extra_info_str
);
374 assert(strlen(tmp_str
) ==
375 (size_t) (tmp_str_ptr
- tmp_str
));
377 char *hex_str
= malloc(strlen(tmp_str
) * 2 + 1);
378 size_t pkt_len
= hexify(hex_str
, (const uint8_t *)tmp_str
,
379 strlen(tmp_str
), strlen(tmp_str
) * 2 + 1);
381 gdb_put_packet(connection
, hex_str
, pkt_len
);
387 gdb_put_packet(connection
, "", 0);
389 } else if (strncmp(packet
, "qSymbol", 7) == 0) {
390 if (rtos_qsymbol(connection
, packet
, packet_size
) == 1) {
391 if (target
->rtos_auto_detect
== true) {
392 target
->rtos_auto_detect
= false;
393 target
->rtos
->type
->create(target
);
395 target
->rtos
->type
->update_threads(target
->rtos
);
398 } else if (strncmp(packet
, "qfThreadInfo", 12) == 0) {
401 if (target
->rtos
->thread_count
== 0) {
402 gdb_put_packet(connection
, "l", 1);
404 /*thread id are 16 char +1 for ',' */
405 char *out_str
= malloc(17 * target
->rtos
->thread_count
+ 1);
406 char *tmp_str
= out_str
;
407 for (i
= 0; i
< target
->rtos
->thread_count
; i
++) {
408 tmp_str
+= sprintf(tmp_str
, "%c%016" PRIx64
, i
== 0 ? 'm' : ',',
409 target
->rtos
->thread_details
[i
].threadid
);
411 gdb_put_packet(connection
, out_str
, strlen(out_str
));
415 gdb_put_packet(connection
, "l", 1);
418 } else if (strncmp(packet
, "qsThreadInfo", 12) == 0) {
419 gdb_put_packet(connection
, "l", 1);
421 } else if (strncmp(packet
, "qAttached", 9) == 0) {
422 gdb_put_packet(connection
, "1", 1);
424 } else if (strncmp(packet
, "qOffsets", 8) == 0) {
425 char offsets
[] = "Text=0;Data=0;Bss=0";
426 gdb_put_packet(connection
, offsets
, sizeof(offsets
)-1);
428 } else if (strncmp(packet
, "qCRC:", 5) == 0) {
429 /* make sure we check this before "qC" packet below
430 * otherwise it gets incorrectly handled */
431 return GDB_THREAD_PACKET_NOT_CONSUMED
;
432 } else if (strncmp(packet
, "qC", 2) == 0) {
436 size
= snprintf(buffer
, 19, "QC%016" PRIx64
, target
->rtos
->current_thread
);
437 gdb_put_packet(connection
, buffer
, size
);
439 gdb_put_packet(connection
, "QC0", 3);
441 } else if (packet
[0] == 'T') { /* Is thread alive? */
444 sscanf(packet
, "T%" SCNx64
, &threadid
);
445 if ((target
->rtos
) && (target
->rtos
->thread_details
)) {
447 for (thread_num
= 0; thread_num
< target
->rtos
->thread_count
; thread_num
++) {
448 if (target
->rtos
->thread_details
[thread_num
].threadid
== threadid
) {
449 if (target
->rtos
->thread_details
[thread_num
].exists
)
455 gdb_put_packet(connection
, "OK", 2); /* thread alive */
457 gdb_put_packet(connection
, "E01", 3); /* thread not found */
459 } else if (packet
[0] == 'H') { /* Set current thread ( 'c' for step and continue, 'g' for
460 * all other operations ) */
461 if ((packet
[1] == 'g') && (target
->rtos
)) {
463 sscanf(packet
, "Hg%16" SCNx64
, &threadid
);
464 LOG_DEBUG("RTOS: GDB requested to set current thread to 0x%" PRIx64
, threadid
);
465 /* threadid of 0 indicates target should choose */
467 target
->rtos
->current_threadid
= target
->rtos
->current_thread
;
469 target
->rtos
->current_threadid
= threadid
;
471 gdb_put_packet(connection
, "OK", 2);
475 return GDB_THREAD_PACKET_NOT_CONSUMED
;
478 static int rtos_put_gdb_reg_list(struct connection
*connection
,
479 struct rtos_reg
*reg_list
, int num_regs
)
481 size_t num_bytes
= 1; /* NUL */
482 for (int i
= 0; i
< num_regs
; ++i
)
483 num_bytes
+= DIV_ROUND_UP(reg_list
[i
].size
, 8) * 2;
485 char *hex
= malloc(num_bytes
);
488 for (int i
= 0; i
< num_regs
; ++i
) {
489 size_t count
= DIV_ROUND_UP(reg_list
[i
].size
, 8);
490 size_t n
= hexify(hex_p
, reg_list
[i
].value
, count
, num_bytes
);
495 gdb_put_packet(connection
, hex
, strlen(hex
));
501 /** Look through all registers to find this register. */
502 int rtos_get_gdb_reg(struct connection
*connection
, int reg_num
)
504 struct target
*target
= get_target_from_connection(connection
);
505 int64_t current_threadid
= target
->rtos
->current_threadid
;
506 if ((target
->rtos
) && (current_threadid
!= -1) &&
507 (current_threadid
!= 0) &&
508 ((current_threadid
!= target
->rtos
->current_thread
) ||
509 (target
->smp
))) { /* in smp several current thread are possible */
510 struct rtos_reg
*reg_list
;
513 LOG_DEBUG("getting register %d for thread 0x%" PRIx64
514 ", target->rtos->current_thread=0x%" PRIx64
,
517 target
->rtos
->current_thread
);
520 if (target
->rtos
->type
->get_thread_reg
) {
521 reg_list
= calloc(1, sizeof(*reg_list
));
523 retval
= target
->rtos
->type
->get_thread_reg(target
->rtos
,
524 current_threadid
, reg_num
, ®_list
[0]);
525 if (retval
!= ERROR_OK
) {
526 LOG_ERROR("RTOS: failed to get register %d", reg_num
);
530 retval
= target
->rtos
->type
->get_thread_reg_list(target
->rtos
,
534 if (retval
!= ERROR_OK
) {
535 LOG_ERROR("RTOS: failed to get register list");
540 for (int i
= 0; i
< num_regs
; ++i
) {
541 if (reg_list
[i
].number
== (uint32_t)reg_num
) {
542 rtos_put_gdb_reg_list(connection
, reg_list
+ i
, 1);
553 /** Return a list of general registers. */
554 int rtos_get_gdb_reg_list(struct connection
*connection
)
556 struct target
*target
= get_target_from_connection(connection
);
557 int64_t current_threadid
= target
->rtos
->current_threadid
;
558 if ((target
->rtos
) && (current_threadid
!= -1) &&
559 (current_threadid
!= 0) &&
560 ((current_threadid
!= target
->rtos
->current_thread
) ||
561 (target
->smp
))) { /* in smp several current thread are possible */
562 struct rtos_reg
*reg_list
;
565 LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
566 ", target->rtos->current_thread=0x%" PRIx64
"\r\n",
568 target
->rtos
->current_thread
);
570 int retval
= target
->rtos
->type
->get_thread_reg_list(target
->rtos
,
574 if (retval
!= ERROR_OK
) {
575 LOG_ERROR("RTOS: failed to get register list");
579 rtos_put_gdb_reg_list(connection
, reg_list
, num_regs
);
587 int rtos_set_reg(struct connection
*connection
, int reg_num
,
590 struct target
*target
= get_target_from_connection(connection
);
591 int64_t current_threadid
= target
->rtos
->current_threadid
;
592 if ((target
->rtos
) &&
593 (target
->rtos
->type
->set_reg
) &&
594 (current_threadid
!= -1) &&
595 (current_threadid
!= 0)) {
596 return target
->rtos
->type
->set_reg(target
->rtos
, reg_num
, reg_value
);
601 int rtos_generic_stack_read(struct target
*target
,
602 const struct rtos_register_stacking
*stacking
,
604 struct rtos_reg
**reg_list
,
609 if (stack_ptr
== 0) {
610 LOG_ERROR("Error: null stack pointer in thread");
614 uint8_t *stack_data
= malloc(stacking
->stack_registers_size
);
615 uint32_t address
= stack_ptr
;
617 if (stacking
->stack_growth_direction
== 1)
618 address
-= stacking
->stack_registers_size
;
619 if (stacking
->read_stack
)
620 retval
= stacking
->read_stack(target
, address
, stacking
, stack_data
);
622 retval
= target_read_buffer(target
, address
, stacking
->stack_registers_size
, stack_data
);
623 if (retval
!= ERROR_OK
) {
625 LOG_ERROR("Error reading stack frame from thread");
628 LOG_DEBUG("RTOS: Read stack frame at 0x%" PRIx32
, address
);
631 LOG_OUTPUT("Stack Data :");
632 for (i
= 0; i
< stacking
->stack_registers_size
; i
++)
633 LOG_OUTPUT("%02X", stack_data
[i
]);
637 target_addr_t new_stack_ptr
;
638 if (stacking
->calculate_process_stack
) {
639 new_stack_ptr
= stacking
->calculate_process_stack(target
,
640 stack_data
, stacking
, stack_ptr
);
642 new_stack_ptr
= stack_ptr
- stacking
->stack_growth_direction
*
643 stacking
->stack_registers_size
;
646 *reg_list
= calloc(stacking
->num_output_registers
, sizeof(struct rtos_reg
));
647 *num_regs
= stacking
->num_output_registers
;
649 for (int i
= 0; i
< stacking
->num_output_registers
; ++i
) {
650 (*reg_list
)[i
].number
= stacking
->register_offsets
[i
].number
;
651 (*reg_list
)[i
].size
= stacking
->register_offsets
[i
].width_bits
;
653 int offset
= stacking
->register_offsets
[i
].offset
;
655 buf_cpy(&new_stack_ptr
, (*reg_list
)[i
].value
, (*reg_list
)[i
].size
);
656 else if (offset
!= -1)
657 buf_cpy(stack_data
+ offset
, (*reg_list
)[i
].value
, (*reg_list
)[i
].size
);
661 /* LOG_OUTPUT("Output register string: %s\r\n", *hex_reg_list); */
665 static int rtos_try_next(struct target
*target
)
667 struct rtos
*os
= target
->rtos
;
668 const struct rtos_type
**type
= rtos_types
;
673 while (*type
&& os
->type
!= *type
)
676 if (!*type
|| !*(++type
))
687 int rtos_update_threads(struct target
*target
)
689 if ((target
->rtos
) && (target
->rtos
->type
))
690 target
->rtos
->type
->update_threads(target
->rtos
);
694 void rtos_free_threadlist(struct rtos
*rtos
)
696 if (rtos
->thread_details
) {
699 for (j
= 0; j
< rtos
->thread_count
; j
++) {
700 struct thread_detail
*current_thread
= &rtos
->thread_details
[j
];
701 free(current_thread
->thread_name_str
);
702 free(current_thread
->extra_info_str
);
704 free(rtos
->thread_details
);
705 rtos
->thread_details
= NULL
;
706 rtos
->thread_count
= 0;
707 rtos
->current_threadid
= -1;
708 rtos
->current_thread
= 0;
712 int rtos_read_buffer(struct target
*target
, target_addr_t address
,
713 uint32_t size
, uint8_t *buffer
)
715 if (target
->rtos
->type
->read_buffer
)
716 return target
->rtos
->type
->read_buffer(target
->rtos
, address
, size
, buffer
);
717 return ERROR_NOT_IMPLEMENTED
;
720 int rtos_write_buffer(struct target
*target
, target_addr_t address
,
721 uint32_t size
, const uint8_t *buffer
)
723 if (target
->rtos
->type
->write_buffer
)
724 return target
->rtos
->type
->write_buffer(target
->rtos
, address
, size
, buffer
);
725 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)