target: remove unused function target_buffer_get_u8()
[openocd.git] / src / rtos / ThreadX.c
1 /***************************************************************************
2 * Copyright (C) 2011 by Broadcom Corporation *
3 * Evan Hunter - ehunter@broadcom.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include <helper/time_support.h>
24 #include <jtag/jtag.h>
25 #include "target/target.h"
26 #include "target/target_type.h"
27 #include "rtos.h"
28 #include "helper/log.h"
29 #include "helper/types.h"
30 #include "rtos_standard_stackings.h"
31
32 static const struct rtos_register_stacking *get_stacking_info(const struct rtos *rtos, int64_t stack_ptr);
33 static const struct rtos_register_stacking *get_stacking_info_arm926ejs(const struct rtos *rtos, int64_t stack_ptr);
34
35 static int is_thread_id_valid(const struct rtos *rtos, int64_t thread_id);
36 static int is_thread_id_valid_arm926ejs(const struct rtos *rtos, int64_t thread_id);
37
38 static bool ThreadX_detect_rtos(struct target *target);
39 static int ThreadX_create(struct target *target);
40 static int ThreadX_update_threads(struct rtos *rtos);
41 static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, struct rtos_reg **reg_list, int *num_regs);
42 static int ThreadX_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
43
44
45
46 struct ThreadX_thread_state {
47 int value;
48 const char *desc;
49 };
50
51 static const struct ThreadX_thread_state ThreadX_thread_states[] = {
52 { 0, "Ready" },
53 { 1, "Completed" },
54 { 2, "Terminated" },
55 { 3, "Suspended" },
56 { 4, "Sleeping" },
57 { 5, "Waiting - Queue" },
58 { 6, "Waiting - Semaphore" },
59 { 7, "Waiting - Event flag" },
60 { 8, "Waiting - Memory" },
61 { 9, "Waiting - Memory" },
62 { 10, "Waiting - I/O" },
63 { 11, "Waiting - Filesystem" },
64 { 12, "Waiting - Network" },
65 { 13, "Waiting - Mutex" },
66 };
67
68 #define THREADX_NUM_STATES (sizeof(ThreadX_thread_states)/sizeof(struct ThreadX_thread_state))
69
70 #define ARM926EJS_REGISTERS_SIZE_SOLICITED (11 * 4)
71 static const struct stack_register_offset rtos_threadx_arm926ejs_stack_offsets_solicited[] = {
72 { 0, -1, 32 }, /* r0 */
73 { 1, -1, 32 }, /* r1 */
74 { 2, -1, 32 }, /* r2 */
75 { 3, -1, 32 }, /* r3 */
76 { 4, 0x08, 32 }, /* r4 */
77 { 5, 0x0C, 32 }, /* r5 */
78 { 6, 0x10, 32 }, /* r6 */
79 { 7, 0x14, 32 }, /* r7 */
80 { 8, 0x18, 32 }, /* r8 */
81 { 9, 0x1C, 32 }, /* r9 */
82 { 10, 0x20, 32 }, /* r10 */
83 { 11, 0x24, 32 }, /* r11 */
84 { 12, -1, 32 }, /* r12 */
85 { 13, -2, 32 }, /* sp (r13) */
86 { 14, 0x28, 32 }, /* lr (r14) */
87 { 15, -1, 32 }, /* pc (r15) */
88 /*{ 16, -1, 32 },*/ /* lr (r14) */
89 /*{ 17, 0x28, 32 },*/ /* pc (r15) */
90 { 16, 0x04, 32 }, /* xPSR */
91 };
92 #define ARM926EJS_REGISTERS_SIZE_INTERRUPT (17 * 4)
93 static const struct stack_register_offset rtos_threadx_arm926ejs_stack_offsets_interrupt[] = {
94 { 0, 0x08, 32 }, /* r0 */
95 { 1, 0x0C, 32 }, /* r1 */
96 { 2, 0x10, 32 }, /* r2 */
97 { 3, 0x14, 32 }, /* r3 */
98 { 4, 0x18, 32 }, /* r4 */
99 { 5, 0x1C, 32 }, /* r5 */
100 { 6, 0x20, 32 }, /* r6 */
101 { 7, 0x24, 32 }, /* r7 */
102 { 8, 0x28, 32 }, /* r8 */
103 { 9, 0x2C, 32 }, /* r9 */
104 { 10, 0x30, 32 }, /* r10 */
105 { 11, 0x34, 32 }, /* r11 */
106 { 12, 0x38, 32 }, /* r12 */
107 { 13, -2, 32 }, /* sp (r13) */
108 { 14, 0x3C, 32 }, /* lr (r14) */
109 { 15, 0x40, 32 }, /* pc (r15) */
110 { 16, 0x04, 32 }, /* xPSR */
111 };
112
113 const struct rtos_register_stacking rtos_threadx_arm926ejs_stacking[] = {
114 {
115 ARM926EJS_REGISTERS_SIZE_SOLICITED, /* stack_registers_size */
116 -1, /* stack_growth_direction */
117 17, /* num_output_registers */
118 NULL, /* stack_alignment */
119 rtos_threadx_arm926ejs_stack_offsets_solicited /* register_offsets */
120 },
121 {
122 ARM926EJS_REGISTERS_SIZE_INTERRUPT, /* stack_registers_size */
123 -1, /* stack_growth_direction */
124 17, /* num_output_registers */
125 NULL, /* stack_alignment */
126 rtos_threadx_arm926ejs_stack_offsets_interrupt /* register_offsets */
127 },
128 };
129
130 struct ThreadX_params {
131 const char *target_name;
132 unsigned char pointer_width;
133 unsigned char thread_stack_offset;
134 unsigned char thread_name_offset;
135 unsigned char thread_state_offset;
136 unsigned char thread_next_offset;
137 const struct rtos_register_stacking *stacking_info;
138 size_t stacking_info_nb;
139 const struct rtos_register_stacking* (*fn_get_stacking_info)(const struct rtos *rtos, int64_t stack_ptr);
140 int (*fn_is_thread_id_valid)(const struct rtos *rtos, int64_t thread_id);
141 };
142
143 static const struct ThreadX_params ThreadX_params_list[] = {
144 {
145 "cortex_m", /* target_name */
146 4, /* pointer_width; */
147 8, /* thread_stack_offset; */
148 40, /* thread_name_offset; */
149 48, /* thread_state_offset; */
150 136, /* thread_next_offset */
151 &rtos_standard_Cortex_M3_stacking, /* stacking_info */
152 1, /* stacking_info_nb */
153 NULL, /* fn_get_stacking_info */
154 NULL, /* fn_is_thread_id_valid */
155 },
156 {
157 "cortex_r4", /* target_name */
158 4, /* pointer_width; */
159 8, /* thread_stack_offset; */
160 40, /* thread_name_offset; */
161 48, /* thread_state_offset; */
162 136, /* thread_next_offset */
163 &rtos_standard_Cortex_R4_stacking, /* stacking_info */
164 1, /* stacking_info_nb */
165 NULL, /* fn_get_stacking_info */
166 NULL, /* fn_is_thread_id_valid */
167 },
168 {
169 "arm926ejs", /* target_name */
170 4, /* pointer_width; */
171 8, /* thread_stack_offset; */
172 40, /* thread_name_offset; */
173 48, /* thread_state_offset; */
174 136, /* thread_next_offset */
175 rtos_threadx_arm926ejs_stacking, /* stacking_info */
176 2, /* stacking_info_nb */
177 get_stacking_info_arm926ejs, /* fn_get_stacking_info */
178 is_thread_id_valid_arm926ejs, /* fn_is_thread_id_valid */
179 },
180 };
181
182 #define THREADX_NUM_PARAMS ((int)(sizeof(ThreadX_params_list)/sizeof(struct ThreadX_params)))
183
184 enum ThreadX_symbol_values {
185 ThreadX_VAL_tx_thread_current_ptr = 0,
186 ThreadX_VAL_tx_thread_created_ptr = 1,
187 ThreadX_VAL_tx_thread_created_count = 2,
188 };
189
190 static const char * const ThreadX_symbol_list[] = {
191 "_tx_thread_current_ptr",
192 "_tx_thread_created_ptr",
193 "_tx_thread_created_count",
194 NULL
195 };
196
197 const struct rtos_type ThreadX_rtos = {
198 .name = "ThreadX",
199
200 .detect_rtos = ThreadX_detect_rtos,
201 .create = ThreadX_create,
202 .update_threads = ThreadX_update_threads,
203 .get_thread_reg_list = ThreadX_get_thread_reg_list,
204 .get_symbol_list_to_lookup = ThreadX_get_symbol_list_to_lookup,
205 };
206
207 static const struct rtos_register_stacking *get_stacking_info(const struct rtos *rtos, int64_t stack_ptr)
208 {
209 const struct ThreadX_params *param = (const struct ThreadX_params *) rtos->rtos_specific_params;
210
211 if (param->fn_get_stacking_info != NULL)
212 return param->fn_get_stacking_info(rtos, stack_ptr);
213
214 return param->stacking_info + 0;
215 }
216
217 static int is_thread_id_valid(const struct rtos *rtos, int64_t thread_id)
218 {
219 const struct ThreadX_params *param;
220
221 if (rtos->rtos_specific_params == NULL)
222 return 0; /* invalid */
223
224 param = (const struct ThreadX_params *) rtos->rtos_specific_params;
225
226 if (param->fn_is_thread_id_valid != NULL)
227 return param->fn_is_thread_id_valid(rtos, thread_id);
228
229 return (thread_id != 0);
230 }
231
232 static const struct rtos_register_stacking *get_stacking_info_arm926ejs(const struct rtos *rtos, int64_t stack_ptr)
233 {
234 const struct ThreadX_params *param = (const struct ThreadX_params *) rtos->rtos_specific_params;
235 int retval;
236 uint32_t flag;
237
238 retval = target_read_buffer(rtos->target,
239 stack_ptr,
240 sizeof(flag),
241 (uint8_t *)&flag);
242 if (retval != ERROR_OK) {
243 LOG_ERROR("Error reading stack data from ThreadX thread: stack_ptr=0x%" PRIx64, stack_ptr);
244 return NULL;
245 }
246
247 if (flag == 0) {
248 LOG_DEBUG(" solicited stack");
249 return param->stacking_info + 0;
250 } else {
251 LOG_DEBUG(" interrupt stack: %u", flag);
252 return param->stacking_info + 1;
253 }
254 }
255
256 static int is_thread_id_valid_arm926ejs(const struct rtos *rtos, int64_t thread_id)
257 {
258 return (thread_id != 0 && thread_id != 1);
259 }
260
261 static int ThreadX_update_threads(struct rtos *rtos)
262 {
263 int retval;
264 int tasks_found = 0;
265 int thread_list_size = 0;
266 const struct ThreadX_params *param;
267
268 if (rtos == NULL)
269 return -1;
270
271 if (rtos->rtos_specific_params == NULL)
272 return -3;
273
274 param = (const struct ThreadX_params *) rtos->rtos_specific_params;
275
276 if (rtos->symbols == NULL) {
277 LOG_ERROR("No symbols for ThreadX");
278 return -4;
279 }
280
281 if (rtos->symbols[ThreadX_VAL_tx_thread_created_count].address == 0) {
282 LOG_ERROR("Don't have the number of threads in ThreadX");
283 return -2;
284 }
285
286 /* read the number of threads */
287 retval = target_read_buffer(rtos->target,
288 rtos->symbols[ThreadX_VAL_tx_thread_created_count].address,
289 4,
290 (uint8_t *)&thread_list_size);
291
292 if (retval != ERROR_OK) {
293 LOG_ERROR("Could not read ThreadX thread count from target");
294 return retval;
295 }
296
297 /* wipe out previous thread details if any */
298 rtos_free_threadlist(rtos);
299
300 /* read the current thread id */
301 retval = target_read_buffer(rtos->target,
302 rtos->symbols[ThreadX_VAL_tx_thread_current_ptr].address,
303 4,
304 (uint8_t *)&rtos->current_thread);
305
306 if (retval != ERROR_OK) {
307 LOG_ERROR("Could not read ThreadX current thread from target");
308 return retval;
309 }
310
311 if ((thread_list_size == 0) || (rtos->current_thread == 0)) {
312 /* Either : No RTOS threads - there is always at least the current execution though */
313 /* OR : No current thread - all threads suspended - show the current execution
314 * of idling */
315 char tmp_str[] = "Current Execution";
316 thread_list_size++;
317 tasks_found++;
318 rtos->thread_details = malloc(
319 sizeof(struct thread_detail) * thread_list_size);
320 rtos->thread_details->threadid = 1;
321 rtos->thread_details->exists = true;
322 rtos->thread_details->extra_info_str = NULL;
323 rtos->thread_details->thread_name_str = malloc(sizeof(tmp_str));
324 strcpy(rtos->thread_details->thread_name_str, tmp_str);
325
326 if (thread_list_size == 0) {
327 rtos->thread_count = 1;
328 return ERROR_OK;
329 }
330 } else {
331 /* create space for new thread details */
332 rtos->thread_details = malloc(
333 sizeof(struct thread_detail) * thread_list_size);
334 }
335
336 /* Read the pointer to the first thread */
337 int64_t thread_ptr = 0;
338 retval = target_read_buffer(rtos->target,
339 rtos->symbols[ThreadX_VAL_tx_thread_created_ptr].address,
340 param->pointer_width,
341 (uint8_t *)&thread_ptr);
342 if (retval != ERROR_OK) {
343 LOG_ERROR("Could not read ThreadX thread location from target");
344 return retval;
345 }
346
347 /* loop over all threads */
348 int64_t prev_thread_ptr = 0;
349 while ((thread_ptr != prev_thread_ptr) && (tasks_found < thread_list_size)) {
350
351 #define THREADX_THREAD_NAME_STR_SIZE (200)
352 char tmp_str[THREADX_THREAD_NAME_STR_SIZE];
353 unsigned int i = 0;
354 int64_t name_ptr = 0;
355
356 /* Save the thread pointer */
357 rtos->thread_details[tasks_found].threadid = thread_ptr;
358
359 /* read the name pointer */
360 retval = target_read_buffer(rtos->target,
361 thread_ptr + param->thread_name_offset,
362 param->pointer_width,
363 (uint8_t *)&name_ptr);
364 if (retval != ERROR_OK) {
365 LOG_ERROR("Could not read ThreadX thread name pointer from target");
366 return retval;
367 }
368
369 /* Read the thread name */
370 retval =
371 target_read_buffer(rtos->target,
372 name_ptr,
373 THREADX_THREAD_NAME_STR_SIZE,
374 (uint8_t *)&tmp_str);
375 if (retval != ERROR_OK) {
376 LOG_ERROR("Error reading thread name from ThreadX target");
377 return retval;
378 }
379 tmp_str[THREADX_THREAD_NAME_STR_SIZE-1] = '\x00';
380
381 if (tmp_str[0] == '\x00')
382 strcpy(tmp_str, "No Name");
383
384 rtos->thread_details[tasks_found].thread_name_str =
385 malloc(strlen(tmp_str)+1);
386 strcpy(rtos->thread_details[tasks_found].thread_name_str, tmp_str);
387
388 /* Read the thread status */
389 int64_t thread_status = 0;
390 retval = target_read_buffer(rtos->target,
391 thread_ptr + param->thread_state_offset,
392 4,
393 (uint8_t *)&thread_status);
394 if (retval != ERROR_OK) {
395 LOG_ERROR("Error reading thread state from ThreadX target");
396 return retval;
397 }
398
399 for (i = 0; (i < THREADX_NUM_STATES) &&
400 (ThreadX_thread_states[i].value != thread_status); i++) {
401 /* empty */
402 }
403
404 const char *state_desc;
405 if (i < THREADX_NUM_STATES)
406 state_desc = ThreadX_thread_states[i].desc;
407 else
408 state_desc = "Unknown state";
409
410 rtos->thread_details[tasks_found].extra_info_str = malloc(strlen(
411 state_desc)+8);
412 sprintf(rtos->thread_details[tasks_found].extra_info_str, "State: %s", state_desc);
413
414 rtos->thread_details[tasks_found].exists = true;
415
416 tasks_found++;
417 prev_thread_ptr = thread_ptr;
418
419 /* Get the location of the next thread structure. */
420 thread_ptr = 0;
421 retval = target_read_buffer(rtos->target,
422 prev_thread_ptr + param->thread_next_offset,
423 param->pointer_width,
424 (uint8_t *) &thread_ptr);
425 if (retval != ERROR_OK) {
426 LOG_ERROR("Error reading next thread pointer in ThreadX thread list");
427 return retval;
428 }
429 }
430
431 rtos->thread_count = tasks_found;
432
433 return 0;
434 }
435
436 static int ThreadX_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
437 struct rtos_reg **reg_list, int *num_regs)
438 {
439 int retval;
440 const struct ThreadX_params *param;
441
442 if (rtos == NULL)
443 return -1;
444
445 if (!is_thread_id_valid(rtos, thread_id))
446 return -2;
447
448 if (rtos->rtos_specific_params == NULL)
449 return -3;
450
451 param = (const struct ThreadX_params *) rtos->rtos_specific_params;
452
453 /* Read the stack pointer */
454 int64_t stack_ptr = 0;
455 retval = target_read_buffer(rtos->target,
456 thread_id + param->thread_stack_offset,
457 param->pointer_width,
458 (uint8_t *)&stack_ptr);
459 if (retval != ERROR_OK) {
460 LOG_ERROR("Error reading stack frame from ThreadX thread");
461 return retval;
462 }
463
464 LOG_INFO("thread: 0x%" PRIx64 ", stack_ptr=0x%" PRIx64, (uint64_t)thread_id, (uint64_t)stack_ptr);
465
466 if (stack_ptr == 0) {
467 LOG_ERROR("null stack pointer in thread");
468 return -5;
469 }
470
471 const struct rtos_register_stacking *stacking_info =
472 get_stacking_info(rtos, stack_ptr);
473
474 if (stacking_info == NULL) {
475 LOG_ERROR("Unknown stacking info for thread id=0x%" PRIx64, (uint64_t)thread_id);
476 return -6;
477 }
478
479 return rtos_generic_stack_read(rtos->target, stacking_info, stack_ptr, reg_list, num_regs);
480 }
481
482 static int ThreadX_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
483 {
484 unsigned int i;
485 *symbol_list = calloc(
486 ARRAY_SIZE(ThreadX_symbol_list), sizeof(symbol_table_elem_t));
487
488 for (i = 0; i < ARRAY_SIZE(ThreadX_symbol_list); i++)
489 (*symbol_list)[i].symbol_name = ThreadX_symbol_list[i];
490
491 return 0;
492 }
493
494 static bool ThreadX_detect_rtos(struct target *target)
495 {
496 if ((target->rtos->symbols != NULL) &&
497 (target->rtos->symbols[ThreadX_VAL_tx_thread_created_ptr].address != 0)) {
498 /* looks like ThreadX */
499 return true;
500 }
501 return false;
502 }
503
504 #if 0
505
506 static int ThreadX_set_current_thread(struct rtos *rtos, threadid_t thread_id)
507 {
508 return 0;
509 }
510
511 static int ThreadX_get_thread_detail(struct rtos *rtos,
512 threadid_t thread_id,
513 struct thread_detail *detail)
514 {
515 unsigned int i = 0;
516 int retval;
517
518 #define THREADX_THREAD_NAME_STR_SIZE (200)
519 char tmp_str[THREADX_THREAD_NAME_STR_SIZE];
520
521 const struct ThreadX_params *param;
522
523 if (rtos == NULL)
524 return -1;
525
526 if (thread_id == 0)
527 return -2;
528
529 if (rtos->rtos_specific_params == NULL)
530 return -3;
531
532 param = (const struct ThreadX_params *) rtos->rtos_specific_params;
533
534 if (rtos->symbols == NULL) {
535 LOG_ERROR("No symbols for ThreadX");
536 return -3;
537 }
538
539 detail->threadid = thread_id;
540
541 int64_t name_ptr = 0;
542 /* read the name pointer */
543 retval = target_read_buffer(rtos->target,
544 thread_id + param->thread_name_offset,
545 param->pointer_width,
546 (uint8_t *)&name_ptr);
547 if (retval != ERROR_OK) {
548 LOG_ERROR("Could not read ThreadX thread name pointer from target");
549 return retval;
550 }
551
552 /* Read the thread name */
553 retval = target_read_buffer(rtos->target,
554 name_ptr,
555 THREADX_THREAD_NAME_STR_SIZE,
556 (uint8_t *)&tmp_str);
557 if (retval != ERROR_OK) {
558 LOG_ERROR("Error reading thread name from ThreadX target");
559 return retval;
560 }
561 tmp_str[THREADX_THREAD_NAME_STR_SIZE-1] = '\x00';
562
563 if (tmp_str[0] == '\x00')
564 strcpy(tmp_str, "No Name");
565
566 detail->thread_name_str = malloc(strlen(tmp_str)+1);
567
568 /* Read the thread status */
569 int64_t thread_status = 0;
570 retval =
571 target_read_buffer(rtos->target,
572 thread_id + param->thread_state_offset,
573 4,
574 (uint8_t *)&thread_status);
575 if (retval != ERROR_OK) {
576 LOG_ERROR("Error reading thread state from ThreadX target");
577 return retval;
578 }
579
580 for (i = 0; (i < THREADX_NUM_STATES) &&
581 (ThreadX_thread_states[i].value != thread_status); i++) {
582 /* empty */
583 }
584
585 char *state_desc;
586 if (i < THREADX_NUM_STATES)
587 state_desc = ThreadX_thread_states[i].desc;
588 else
589 state_desc = "Unknown state";
590
591 detail->extra_info_str = malloc(strlen(state_desc)+1);
592
593 detail->exists = true;
594
595 return 0;
596 }
597
598 #endif
599
600 static int ThreadX_create(struct target *target)
601 {
602 int i = 0;
603 while ((i < THREADX_NUM_PARAMS) &&
604 (0 != strcmp(ThreadX_params_list[i].target_name, target->type->name))) {
605 i++;
606 }
607 if (i >= THREADX_NUM_PARAMS) {
608 LOG_ERROR("Could not find target in ThreadX compatibility list");
609 return -1;
610 }
611
612 target->rtos->rtos_specific_params = (void *) &ThreadX_params_list[i];
613 target->rtos->current_thread = 0;
614 target->rtos->thread_details = NULL;
615 return 0;
616 }

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)