target/espressif: read entry addresses of pre-defined stub functions
[openocd.git] / src / target / espressif / esp32_sysview.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 * ESP32 sysview tracing module *
5 * Copyright (C) 2020 Espressif Systems Ltd. *
6 ***************************************************************************/
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include <helper/log.h>
13 #include "esp32_apptrace.h"
14 #include "esp32_sysview.h"
15 #include "segger_sysview.h"
16
17 /* in SystemView mode core ID is passed in event ID field */
18 #define ESP32_SYSVIEW_USER_BLOCK_CORE(_v_) (0) /* not used */
19 #define ESP32_SYSVIEW_USER_BLOCK_LEN(_v_) (_v_)
20 #define ESP32_SYSVIEW_USER_BLOCK_HDR_SZ 2
21
22 struct esp_sysview_target2host_hdr {
23 uint8_t block_sz;
24 uint8_t wr_sz;
25 };
26 #define SYSVIEW_BLOCK_SIZE_OFFSET 0
27 #define SYSVIEW_WR_SIZE_OFFSET 1
28
29 static int esp_sysview_trace_header_write(struct esp32_apptrace_cmd_ctx *ctx, bool mcore_format);
30 static int esp32_sysview_core_id_get(struct target *target, uint8_t *hdr_buf);
31 static uint32_t esp32_sysview_usr_block_len_get(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len);
32
33 int esp32_sysview_cmd_init(struct esp32_apptrace_cmd_ctx *cmd_ctx,
34 struct command_invocation *cmd,
35 int mode,
36 bool mcore_format,
37 const char **argv,
38 int argc)
39 {
40 struct esp32_sysview_cmd_data *cmd_data;
41
42 if (argc < 1) {
43 command_print(cmd, "Not enough args! Need trace data destination!");
44 return ERROR_FAIL;
45 }
46
47 int res = esp32_apptrace_cmd_ctx_init(cmd_ctx, cmd, mode);
48 if (res != ERROR_OK)
49 return res;
50
51 int core_num = cmd_ctx->cores_num;
52
53 if (!mcore_format && argc < core_num) {
54 command_print(cmd, "Not enough args! Need %d trace data destinations!", core_num);
55 res = ERROR_FAIL;
56 goto on_error;
57 }
58
59 cmd_data = calloc(1, sizeof(*cmd_data));
60 if (!cmd_data) {
61 command_print(cmd, "No memory for command data!");
62 res = ERROR_FAIL;
63 goto on_error;
64 }
65 cmd_ctx->cmd_priv = cmd_data;
66 cmd_data->mcore_format = mcore_format;
67
68 /*outfile1 [outfile2] [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]] */
69 int dests_num = esp32_apptrace_dest_init(cmd_data->data_dests, argv, !mcore_format ? core_num : 1);
70 if (!mcore_format && dests_num < core_num) {
71 command_print(cmd, "Not enough args! Need %d trace data destinations!", core_num);
72 free(cmd_data);
73 res = ERROR_FAIL;
74 goto on_error;
75 }
76 cmd_data->apptrace.max_len = UINT32_MAX;
77 cmd_data->apptrace.poll_period = 0 /*ms*/;
78 cmd_ctx->stop_tmo = -1.0; /* infinite */
79 if (argc > dests_num) {
80 /* parse remaining args */
81 esp32_apptrace_cmd_args_parse(cmd_ctx,
82 &cmd_data->apptrace,
83 &argv[dests_num],
84 argc - dests_num);
85 }
86 LOG_USER("App trace params: from %d cores, size %u bytes, stop_tmo %g s, "
87 "poll period %u ms, wait_rst %d, skip %u bytes",
88 cmd_ctx->cores_num,
89 cmd_data->apptrace.max_len,
90 cmd_ctx->stop_tmo,
91 cmd_data->apptrace.poll_period,
92 cmd_data->apptrace.wait4halt,
93 cmd_data->apptrace.skip_len);
94
95 cmd_ctx->trace_format.hdr_sz = ESP32_SYSVIEW_USER_BLOCK_HDR_SZ;
96 cmd_ctx->trace_format.core_id_get = esp32_sysview_core_id_get;
97 cmd_ctx->trace_format.usr_block_len_get = esp32_sysview_usr_block_len_get;
98
99 res = esp_sysview_trace_header_write(cmd_ctx, mcore_format);
100 if (res != ERROR_OK) {
101 command_print(cmd, "Failed to write trace header (%d)!", res);
102 esp32_apptrace_dest_cleanup(cmd_data->data_dests, core_num);
103 free(cmd_data);
104 return res;
105 }
106 return ERROR_OK;
107 on_error:
108 cmd_ctx->running = 0;
109 esp32_apptrace_cmd_ctx_cleanup(cmd_ctx);
110 return res;
111 }
112
113 int esp32_sysview_cmd_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx)
114 {
115 struct esp32_sysview_cmd_data *cmd_data = cmd_ctx->cmd_priv;
116
117 esp32_apptrace_dest_cleanup(cmd_data->data_dests, cmd_ctx->cores_num);
118 free(cmd_data);
119 cmd_ctx->cmd_priv = NULL;
120 esp32_apptrace_cmd_ctx_cleanup(cmd_ctx);
121 return ERROR_OK;
122 }
123
124 static int esp32_sysview_core_id_get(struct target *target, uint8_t *hdr_buf)
125 {
126 /* for sysview compressed apptrace header is used, so core id is encoded in sysview packet */
127 return 0;
128 }
129
130 static uint32_t esp32_sysview_usr_block_len_get(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len)
131 {
132 *wr_len = ESP32_SYSVIEW_USER_BLOCK_LEN(hdr_buf[SYSVIEW_WR_SIZE_OFFSET]);
133 return ESP32_SYSVIEW_USER_BLOCK_LEN(hdr_buf[SYSVIEW_BLOCK_SIZE_OFFSET]);
134 }
135
136 static int esp_sysview_trace_header_write(struct esp32_apptrace_cmd_ctx *ctx, bool mcore_format)
137 {
138 struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv;
139 char *hdr_str;
140 int dests_num;
141
142 if (!mcore_format) {
143 hdr_str = ";\n"
144 "; Version " SYSVIEW_MIN_VER_STRING "\n"
145 "; Author Espressif Inc\n"
146 ";\n";
147 dests_num = ctx->cores_num;
148 } else {
149 hdr_str = ";\n"
150 "; Version " SYSVIEW_MIN_VER_STRING "\n"
151 "; Author Espressif Inc\n"
152 "; ESP_Extension\n"
153 ";\n";
154 dests_num = 1;
155 }
156
157 int hdr_len = strlen(hdr_str);
158 for (int i = 0; i < dests_num; i++) {
159 int res = cmd_data->data_dests[i].write(cmd_data->data_dests[i].priv,
160 (uint8_t *)hdr_str,
161 hdr_len);
162 if (res != ERROR_OK) {
163 LOG_ERROR("sysview: Failed to write %u bytes to dest %d!", hdr_len, i);
164 return ERROR_FAIL;
165 }
166 }
167 return ERROR_OK;
168 }
169
170 static void sysview_encode_u32(uint8_t **dest, uint32_t val)
171 {
172 uint8_t *sv_ptr = *dest;
173 while (val > 0x7F) {
174 *sv_ptr++ = (uint8_t)(val | 0x80);
175 val >>= 7;
176 }
177 *sv_ptr++ = (uint8_t)val;
178 *dest = sv_ptr;
179 }
180
181 static uint32_t esp_sysview_decode_u32(uint8_t **ptr)
182 {
183 uint32_t val = 0;
184 for (int k = 0;; k++, (*ptr)++) {
185 if (**ptr & 0x80) {
186 val |= (uint32_t)(**ptr & ~0x80) << 7 * k;
187 } else {
188 val |= (uint32_t)**ptr << 7 * k;
189 (*ptr)++;
190 break;
191 }
192 }
193 return val;
194 }
195
196 static uint16_t esp_sysview_decode_plen(uint8_t **ptr)
197 {
198 uint16_t payload_len = 0;
199 uint8_t *p = *ptr;
200 /* here pkt points to encoded payload length */
201 if (*p & 0x80) {
202 payload_len = *(p + 1); /* higher part */
203 payload_len = (payload_len << 7) | (*p & ~0x80);/* lower 7 bits */
204 p += 2; /* payload len (2 bytes) */
205 } else {
206 payload_len = *p;
207 p++; /* payload len (1 byte) */
208 }
209 *ptr = p;
210
211 return payload_len;
212 }
213
214 static uint16_t esp_sysview_get_predef_payload_len(uint16_t id, uint8_t *pkt)
215 {
216 uint16_t len;
217 uint8_t *ptr = pkt;
218
219 switch (id) {
220 case SYSVIEW_EVTID_OVERFLOW:
221 case SYSVIEW_EVTID_ISR_ENTER:
222 case SYSVIEW_EVTID_TASK_START_EXEC:
223 case SYSVIEW_EVTID_TASK_START_READY:
224 case SYSVIEW_EVTID_TASK_CREATE:
225 case SYSVIEW_EVTID_SYSTIME_CYCLES:
226 case SYSVIEW_EVTID_USER_START:
227 case SYSVIEW_EVTID_USER_STOP:
228 case SYSVIEW_EVTID_TIMER_ENTER:
229 /*ENCODE_U32 */
230 esp_sysview_decode_u32(&ptr);
231 len = ptr - pkt;
232 break;
233 case SYSVIEW_EVTID_TASK_STOP_READY:
234 case SYSVIEW_EVTID_SYSTIME_US:
235 /*2*ENCODE_U32 */
236 esp_sysview_decode_u32(&ptr);
237 esp_sysview_decode_u32(&ptr);
238 len = ptr - pkt;
239 break;
240 case SYSVIEW_EVTID_SYSDESC:
241 /*str(128 + 1) */
242 len = *ptr + 1;
243 break;
244 case SYSVIEW_EVTID_TASK_INFO:
245 case SYSVIEW_EVTID_MODULEDESC:
246 /*2*ENCODE_U32 + str */
247 esp_sysview_decode_u32(&ptr);
248 esp_sysview_decode_u32(&ptr);
249 /* TODO: add support for strings longer then 255 bytes */
250 len = ptr - pkt + *ptr + 1;
251 break;
252 case SYSVIEW_EVTID_STACK_INFO:
253 /*4*ENCODE_U32 */
254 esp_sysview_decode_u32(&ptr);
255 esp_sysview_decode_u32(&ptr);
256 esp_sysview_decode_u32(&ptr);
257 esp_sysview_decode_u32(&ptr);
258 len = ptr - pkt;
259 break;
260 case SYSVIEW_EVTID_ISR_EXIT:
261 case SYSVIEW_EVTID_TASK_STOP_EXEC:
262 case SYSVIEW_EVTID_TRACE_START:
263 case SYSVIEW_EVTID_TRACE_STOP:
264 case SYSVIEW_EVTID_IDLE:
265 case SYSVIEW_EVTID_ISR_TO_SCHEDULER:
266 case SYSVIEW_EVTID_TIMER_EXIT:
267 len = 0;
268 break;
269
270 /*case SYSVIEW_EVTID_NOP: */
271 default:
272 LOG_ERROR("sysview: Unsupported predef event %d!", id);
273 len = 0;
274 }
275 return len;
276 }
277
278 static uint16_t esp_sysview_parse_packet(uint8_t *pkt_buf,
279 uint32_t *pkt_len,
280 unsigned int *pkt_core_id,
281 uint32_t *delta,
282 uint32_t *delta_len,
283 bool clear_core_bit)
284 {
285 uint8_t *pkt = pkt_buf;
286 uint16_t event_id = 0, payload_len = 0;
287
288 *pkt_core_id = 0;
289 *pkt_len = 0;
290 /* 1-2 byte of message type, 0-2 byte of payload length, payload, 1-5 bytes of timestamp. */
291 if (*pkt & 0x80) {
292 if (*(pkt + 1) & (1 << 6)) {
293 if (clear_core_bit)
294 *(pkt + 1) &= ~(1 << 6); /* clear core_id bit */
295 *pkt_core_id = 1;
296 }
297 event_id = *(pkt + 1) & ~(1 << 6); /* higher part */
298 event_id = (event_id << 7) | (*pkt & ~0x80); /* lower 7 bits */
299 pkt += 2; /* event_id (2 bytes) */
300 /* here pkt points to encoded payload length */
301 payload_len = esp_sysview_decode_plen(&pkt);
302 } else {
303 if (*pkt & (1 << 6)) {
304 if (clear_core_bit)
305 *pkt &= ~(1 << 6); /* clear core_id bit */
306 *pkt_core_id = 1;
307 }
308 /* event_id (1 byte) */
309 event_id = *pkt & ~(1 << 6);
310 pkt++;
311 if (event_id < 24)
312 payload_len = esp_sysview_get_predef_payload_len(event_id, pkt);
313 else
314 payload_len = esp_sysview_decode_plen(&pkt);
315 }
316 pkt += payload_len;
317 uint8_t *delta_start = pkt;
318 *delta = esp_sysview_decode_u32(&pkt);
319 *delta_len = pkt - delta_start;
320 *pkt_len = pkt - pkt_buf;
321 LOG_DEBUG("sysview: evt %d len %d plen %d dlen %d",
322 event_id,
323 *pkt_len,
324 payload_len,
325 *delta_len);
326 return event_id;
327 }
328
329 static int esp32_sysview_write_packet(struct esp32_sysview_cmd_data *cmd_data,
330 int pkt_core_id, uint32_t pkt_len, uint8_t *pkt_buf, uint32_t delta_len, uint8_t *delta_buf)
331 {
332 if (!cmd_data->data_dests[pkt_core_id].write)
333 return ERROR_FAIL;
334
335 int res = cmd_data->data_dests[pkt_core_id].write(cmd_data->data_dests[pkt_core_id].priv, pkt_buf, pkt_len);
336
337 if (res != ERROR_OK) {
338 LOG_ERROR("sysview: Failed to write %u bytes to dest %d!", pkt_len, pkt_core_id);
339 return res;
340 }
341 if (delta_len) {
342 /* write packet with modified delta */
343 res = cmd_data->data_dests[pkt_core_id].write(cmd_data->data_dests[pkt_core_id].priv, delta_buf, delta_len);
344 if (res != ERROR_OK) {
345 LOG_ERROR("sysview: Failed to write %u bytes of delta to dest %d!", delta_len, pkt_core_id);
346 return res;
347 }
348 }
349 return ERROR_OK;
350 }
351
352 static int esp32_sysview_process_packet(struct esp32_apptrace_cmd_ctx *ctx,
353 unsigned int pkt_core_id, uint16_t event_id, uint32_t delta, uint32_t delta_len,
354 uint32_t pkt_len, uint8_t *pkt_buf)
355 {
356 struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv;
357 int pkt_core_changed = 0;
358 uint32_t new_delta_len = 0;
359 uint8_t new_delta_buf[10];
360 uint32_t wr_len = pkt_len;
361
362 if (ctx->cores_num > 1) {
363 if (cmd_data->sv_last_core_id == pkt_core_id) {
364 /* if this packet is for the same core as the prev one acc delta and write packet unmodified */
365 cmd_data->sv_acc_time_delta += delta;
366 } else {
367 /* if this packet is for another core then prev one set acc delta to the packet's delta */
368 uint8_t *delta_ptr = new_delta_buf;
369 sysview_encode_u32(&delta_ptr, delta + cmd_data->sv_acc_time_delta);
370 cmd_data->sv_acc_time_delta = delta;
371 wr_len -= delta_len;
372 new_delta_len = delta_ptr - new_delta_buf;
373 pkt_core_changed = 1;
374 }
375 cmd_data->sv_last_core_id = pkt_core_id;
376 }
377 if (pkt_core_id >= ctx->cores_num) {
378 LOG_WARNING("sysview: invalid core ID in packet %d, must be less then %d! Event id %d",
379 pkt_core_id,
380 ctx->cores_num,
381 event_id);
382 return ERROR_FAIL;
383 }
384 int res = esp32_sysview_write_packet(cmd_data,
385 pkt_core_id,
386 wr_len,
387 pkt_buf,
388 new_delta_len,
389 new_delta_buf);
390 if (res != ERROR_OK)
391 return res;
392 for (unsigned int i = 0; i < ctx->cores_num; i++) {
393 if (pkt_core_id == i)
394 continue;
395 switch (event_id) {
396 /* messages below should be sent to trace destinations for all cores */
397 case SYSVIEW_EVTID_TRACE_START:
398 case SYSVIEW_EVTID_TRACE_STOP:
399 case SYSVIEW_EVTID_SYSTIME_CYCLES:
400 case SYSVIEW_EVTID_SYSTIME_US:
401 case SYSVIEW_EVTID_SYSDESC:
402 case SYSVIEW_EVTID_TASK_INFO:
403 case SYSVIEW_EVTID_STACK_INFO:
404 case SYSVIEW_EVTID_MODULEDESC:
405 case SYSVIEW_EVTID_INIT:
406 case SYSVIEW_EVTID_NUMMODULES:
407 case SYSVIEW_EVTID_OVERFLOW:
408 case SYSVIEW_EVTID_TASK_START_READY:
409 /* if packet's source core has changed */
410 wr_len = pkt_len;
411 if (pkt_core_changed) {
412 /* clone packet with unmodified delta */
413 new_delta_len = 0;
414 } else {
415 /* clone packet with modified delta */
416 uint8_t *delta_ptr = new_delta_buf;
417 sysview_encode_u32(&delta_ptr, cmd_data->sv_acc_time_delta /*delta has been accumulated above*/);
418 wr_len -= delta_len;
419 new_delta_len = delta_ptr - new_delta_buf;
420 }
421 LOG_DEBUG("sysview: Redirect %d bytes of event %d to dest %d", wr_len, event_id, i);
422 res = esp32_sysview_write_packet(cmd_data,
423 i,
424 wr_len,
425 pkt_buf,
426 new_delta_len,
427 new_delta_buf);
428 if (res != ERROR_OK)
429 return res;
430 /* messages above are cloned to trace files for both cores,
431 * so reset acc time delta, both files have actual delta
432 * info */
433 cmd_data->sv_acc_time_delta = 0;
434 break;
435 default:
436 break;
437 }
438 }
439 return ERROR_OK;
440 }
441
442 int esp32_sysview_process_data(struct esp32_apptrace_cmd_ctx *ctx,
443 unsigned int core_id,
444 uint8_t *data,
445 uint32_t data_len)
446 {
447 struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv;
448
449 LOG_DEBUG("sysview: Read from target %d bytes [%x %x %x %x]",
450 data_len,
451 data[0],
452 data[1],
453 data[2],
454 data[3]);
455 int res;
456 uint32_t processed = 0;
457 if (core_id >= ctx->cores_num) {
458 LOG_ERROR("sysview: Invalid core id %d in user block!", core_id);
459 return ERROR_FAIL;
460 }
461 if (cmd_data->mcore_format)
462 core_id = 0;
463 if (ctx->tot_len == 0) {
464 /* handle sync seq */
465 if (data_len < SYSVIEW_SYNC_LEN) {
466 LOG_ERROR("sysview: Invalid init seq len %d!", data_len);
467 return ERROR_FAIL;
468 }
469 LOG_DEBUG("sysview: Process %d sync bytes", SYSVIEW_SYNC_LEN);
470 uint8_t sync_seq[SYSVIEW_SYNC_LEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
471 if (memcmp(data, sync_seq, SYSVIEW_SYNC_LEN) != 0) {
472 LOG_ERROR("sysview: Invalid init seq [%x %x %x %x %x %x %x %x %x %x]",
473 data[0], data[1], data[2], data[3], data[4], data[5], data[6],
474 data[7], data[8], data[9]);
475 return ERROR_FAIL;
476 }
477 res = cmd_data->data_dests[core_id].write(cmd_data->data_dests[core_id].priv,
478 data,
479 SYSVIEW_SYNC_LEN);
480 if (res != ERROR_OK) {
481 LOG_ERROR("sysview: Failed to write %u sync bytes to dest %d!",
482 SYSVIEW_SYNC_LEN,
483 core_id);
484 return res;
485 }
486 if (!cmd_data->mcore_format) {
487 for (unsigned int i = 0; i < ctx->cores_num; i++) {
488 if (core_id == i)
489 continue;
490 res =
491 cmd_data->data_dests[i].write(cmd_data->data_dests[i].priv,
492 data,
493 SYSVIEW_SYNC_LEN);
494 if (res != ERROR_OK) {
495 LOG_ERROR("sysview: Failed to write %u sync bytes to dest %d!", SYSVIEW_SYNC_LEN, core_id ? 0 : 1);
496 return res;
497 }
498 }
499 }
500 ctx->tot_len += SYSVIEW_SYNC_LEN;
501 processed += SYSVIEW_SYNC_LEN;
502 }
503 while (processed < data_len) {
504 unsigned int pkt_core_id;
505 uint32_t delta_len = 0;
506 uint32_t pkt_len = 0, delta = 0;
507 uint16_t event_id = esp_sysview_parse_packet(data + processed,
508 &pkt_len,
509 &pkt_core_id,
510 &delta,
511 &delta_len,
512 !cmd_data->mcore_format);
513 LOG_DEBUG("sysview: Process packet: core %d, %d id, %d bytes [%x %x %x %x]",
514 pkt_core_id,
515 event_id,
516 pkt_len,
517 data[processed + 0],
518 data[processed + 1],
519 data[processed + 2],
520 data[processed + 3]);
521 if (!cmd_data->mcore_format) {
522 res = esp32_sysview_process_packet(ctx,
523 pkt_core_id,
524 event_id,
525 delta,
526 delta_len,
527 pkt_len,
528 data + processed);
529 if (res != ERROR_OK)
530 return res;
531 } else {
532 res = cmd_data->data_dests[0].write(cmd_data->data_dests[0].priv, data + processed, pkt_len);
533 if (res != ERROR_OK) {
534 LOG_ERROR("sysview: Failed to write %u bytes to dest %d!", pkt_len, 0);
535 return res;
536 }
537 }
538 if (event_id == SYSVIEW_EVTID_TRACE_STOP)
539 cmd_data->sv_trace_running = 0;
540 ctx->tot_len += pkt_len;
541 processed += pkt_len;
542 }
543 LOG_USER("%u ", ctx->tot_len);
544 /* check for stop condition */
545 if (ctx->tot_len > cmd_data->apptrace.skip_len &&
546 (ctx->tot_len - cmd_data->apptrace.skip_len >= cmd_data->apptrace.max_len)) {
547 ctx->running = 0;
548 if (duration_measure(&ctx->read_time) != 0) {
549 LOG_ERROR("Failed to stop trace read time measure!");
550 return ERROR_FAIL;
551 }
552 }
553 return ERROR_OK;
554 }

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)