jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / target / espressif / esp_xtensa_apptrace.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 * Xtensa application tracing module for OpenOCD *
5 * Copyright (C) 2017 Espressif Systems Ltd. *
6 ***************************************************************************/
7
8 /*
9 How it works?
10 https://github.com/espressif/esp-idf/blob/master/components/app_trace/port/xtensa/port.c#L8
11 */
12
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16
17 #include <helper/align.h>
18 #include <target/xtensa/xtensa.h>
19 #include <target/xtensa/xtensa_debug_module.h>
20 #include "esp_xtensa_apptrace.h"
21
22 /* TRAX is disabled, so we use its registers for our own purposes
23 * | 31..XXXXXX..24 | 23 .(host_connect). 23 | 22 .(host_data). 22| 21..(block_id)..15 | 14..(block_len)..0 |
24 */
25 #define XTENSA_APPTRACE_CTRL_REG XDMREG_DELAYCNT
26 #define XTENSA_APPTRACE_BLOCK_ID_MSK 0x7FUL
27 #define XTENSA_APPTRACE_BLOCK_ID_MAX XTENSA_APPTRACE_BLOCK_ID_MSK
28 /* if non-zero then apptrace code entered the critical section and the value is an address of the
29 * critical section's exit point */
30 #define XTENSA_APPTRACE_STAT_REG XDMREG_TRIGGERPC
31
32 #define XTENSA_APPTRACE_BLOCK_LEN_MSK 0x7FFFUL
33 #define XTENSA_APPTRACE_BLOCK_LEN(_l_) ((_l_) & XTENSA_APPTRACE_BLOCK_LEN_MSK)
34 #define XTENSA_APPTRACE_BLOCK_LEN_GET(_v_) ((_v_) & XTENSA_APPTRACE_BLOCK_LEN_MSK)
35 #define XTENSA_APPTRACE_BLOCK_ID(_id_) (((_id_) & XTENSA_APPTRACE_BLOCK_ID_MSK) << 15)
36 #define XTENSA_APPTRACE_BLOCK_ID_GET(_v_) (((_v_) >> 15) & XTENSA_APPTRACE_BLOCK_ID_MSK)
37 #define XTENSA_APPTRACE_HOST_DATA BIT(22)
38 #define XTENSA_APPTRACE_HOST_CONNECT BIT(23)
39
40 static int esp_xtensa_apptrace_leave_crit_section_start(struct target *target);
41 static int esp_xtensa_apptrace_leave_crit_section_stop(struct target *target);
42 static int esp_xtensa_apptrace_buffs_write(struct target *target,
43 uint32_t bufs_num,
44 uint32_t buf_sz[],
45 const uint8_t *bufs[],
46 uint32_t block_id,
47 bool ack,
48 bool data);
49
50 struct esp32_apptrace_hw esp_xtensa_apptrace_hw = {
51 .max_block_id = XTENSA_APPTRACE_BLOCK_ID_MAX,
52 .max_block_size_get = esp_xtensa_apptrace_block_max_size_get,
53 .status_reg_read = esp_xtensa_apptrace_status_reg_read,
54 .ctrl_reg_write = esp_xtensa_apptrace_ctrl_reg_write,
55 .ctrl_reg_read = esp_xtensa_apptrace_ctrl_reg_read,
56 .data_len_read = esp_xtensa_apptrace_data_len_read,
57 .data_read = esp_xtensa_apptrace_data_read,
58 .usr_block_max_size_get = esp_xtensa_apptrace_usr_block_max_size_get,
59 .buffs_write = esp_xtensa_apptrace_buffs_write,
60 .leave_trace_crit_section_start = esp_xtensa_apptrace_leave_crit_section_start,
61 .leave_trace_crit_section_stop = esp_xtensa_apptrace_leave_crit_section_stop,
62 };
63
64 uint32_t esp_xtensa_apptrace_block_max_size_get(struct target *target)
65 {
66 struct xtensa *xtensa = target_to_xtensa(target);
67 struct xtensa_trace_status trace_status;
68 struct xtensa_trace_config trace_config;
69 uint32_t max_trace_block_sz;
70
71 int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
72 if (res != ERROR_OK) {
73 LOG_ERROR("Failed to read TRAX status (%d)!", res);
74 return 0;
75 }
76
77 max_trace_block_sz = BIT(((trace_status.stat >> 8) & 0x1f) - 2) * 4;
78 res = xtensa_dm_trace_config_read(&xtensa->dbg_mod, &trace_config);
79 if (res != ERROR_OK) {
80 LOG_ERROR("Failed to read TRAX config (%d)!", res);
81 return 0;
82 }
83 LOG_DEBUG("ctrl=0x%" PRIx32 " memadrstart=0x%" PRIx32 " memadrend=0x%" PRIx32 " traxadr=0x%" PRIx32,
84 trace_config.ctrl,
85 trace_config.memaddr_start,
86 trace_config.memaddr_end,
87 trace_config.addr);
88
89 return max_trace_block_sz;
90 }
91
92 uint32_t esp_xtensa_apptrace_usr_block_max_size_get(struct target *target)
93 {
94 return esp_xtensa_apptrace_block_max_size_get(target) - sizeof(struct esp_apptrace_host2target_hdr);
95 }
96
97 int esp_xtensa_apptrace_data_len_read(struct target *target,
98 uint32_t *block_id,
99 uint32_t *len)
100 {
101 return esp_xtensa_apptrace_ctrl_reg_read(target, block_id, len, NULL);
102 }
103
104 int esp_xtensa_apptrace_usr_block_write(struct target *target,
105 uint32_t block_id,
106 const uint8_t *data,
107 uint32_t size)
108 {
109 return esp_apptrace_usr_block_write(&esp_xtensa_apptrace_hw, target, block_id, data, size);
110 }
111
112 static int esp_xtensa_apptrace_data_reverse_read(struct xtensa *xtensa,
113 uint32_t size,
114 uint8_t *buffer,
115 uint8_t *unal_bytes)
116 {
117 int res = 0;
118 uint32_t rd_sz = ALIGN_UP(size, 4);
119
120 res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXADDR, (xtensa->core_config->trace.mem_sz - rd_sz) / 4);
121 if (res != ERROR_OK)
122 return res;
123 if (!IS_ALIGNED(size, 4)) {
124 res = xtensa_queue_dbg_reg_read(xtensa, XDMREG_TRAXDATA, unal_bytes);
125 if (res != ERROR_OK)
126 return res;
127 }
128 for (unsigned int i = size / 4; i != 0; i--) {
129 res = xtensa_queue_dbg_reg_read(xtensa, XDMREG_TRAXDATA, &buffer[(i - 1) * 4]);
130 if (res != ERROR_OK)
131 return res;
132 }
133 return ERROR_OK;
134 }
135
136 static int esp_xtensa_apptrace_data_normal_read(struct xtensa *xtensa,
137 uint32_t size,
138 uint8_t *buffer,
139 uint8_t *unal_bytes)
140 {
141 int res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXADDR, 0);
142 if (res != ERROR_OK)
143 return res;
144 for (unsigned int i = 0; i < size / 4; i++) {
145 res = xtensa_queue_dbg_reg_read(xtensa, XDMREG_TRAXDATA, &buffer[i * 4]);
146 if (res != ERROR_OK)
147 return res;
148 }
149 if (!IS_ALIGNED(size, 4)) {
150 res = xtensa_queue_dbg_reg_read(xtensa, XDMREG_TRAXDATA, unal_bytes);
151 if (res != ERROR_OK)
152 return res;
153 }
154 return ERROR_OK;
155 }
156
157 int esp_xtensa_apptrace_data_read(struct target *target,
158 uint32_t size,
159 uint8_t *buffer,
160 uint32_t block_id,
161 bool ack)
162 {
163 struct xtensa *xtensa = target_to_xtensa(target);
164 int res;
165 uint32_t tmp = XTENSA_APPTRACE_HOST_CONNECT | XTENSA_APPTRACE_BLOCK_ID(block_id) |
166 XTENSA_APPTRACE_BLOCK_LEN(0);
167 uint8_t unal_bytes[4];
168
169 LOG_DEBUG("Read data on target (%s)", target_name(target));
170 if (xtensa->core_config->trace.reversed_mem_access)
171 res = esp_xtensa_apptrace_data_reverse_read(xtensa, size, buffer, unal_bytes);
172 else
173 res = esp_xtensa_apptrace_data_normal_read(xtensa, size, buffer, unal_bytes);
174 if (res != ERROR_OK)
175 return res;
176 if (ack) {
177 LOG_DEBUG("Ack block %" PRIu32 " target (%s)!", block_id, target_name(target));
178 res = xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
179 if (res != ERROR_OK)
180 return res;
181 }
182 xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
183 res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
184 if (res != ERROR_OK) {
185 LOG_ERROR("Failed to exec JTAG queue!");
186 return res;
187 }
188 if (!IS_ALIGNED(size, 4)) {
189 /* copy the last unaligned bytes */
190 memcpy(buffer + ALIGN_DOWN(size, 4), unal_bytes, size & 0x3UL);
191 }
192 return ERROR_OK;
193 }
194
195 int esp_xtensa_apptrace_ctrl_reg_write(struct target *target,
196 uint32_t block_id,
197 uint32_t len,
198 bool conn,
199 bool data)
200 {
201 struct xtensa *xtensa = target_to_xtensa(target);
202 uint32_t tmp = (conn ? XTENSA_APPTRACE_HOST_CONNECT : 0) |
203 (data ? XTENSA_APPTRACE_HOST_DATA : 0) | XTENSA_APPTRACE_BLOCK_ID(block_id) |
204 XTENSA_APPTRACE_BLOCK_LEN(len);
205
206 xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
207 xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
208 int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
209 if (res != ERROR_OK) {
210 LOG_ERROR("Failed to exec JTAG queue!");
211 return res;
212 }
213
214 return ERROR_OK;
215 }
216
217 int esp_xtensa_apptrace_ctrl_reg_read(struct target *target,
218 uint32_t *block_id,
219 uint32_t *len,
220 bool *conn)
221 {
222 struct xtensa *xtensa = target_to_xtensa(target);
223 uint8_t tmp[4];
224
225 xtensa_queue_dbg_reg_read(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
226 xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
227 int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
228 if (res != ERROR_OK)
229 return res;
230 uint32_t val = target_buffer_get_u32(target, tmp);
231 if (block_id)
232 *block_id = XTENSA_APPTRACE_BLOCK_ID_GET(val);
233 if (len)
234 *len = XTENSA_APPTRACE_BLOCK_LEN_GET(val);
235 if (conn)
236 *conn = val & XTENSA_APPTRACE_HOST_CONNECT;
237 return ERROR_OK;
238 }
239
240 int esp_xtensa_apptrace_status_reg_read(struct target *target, uint32_t *stat)
241 {
242 struct xtensa *xtensa = target_to_xtensa(target);
243 uint8_t tmp[4];
244
245 int res = xtensa_queue_dbg_reg_read(xtensa, XTENSA_APPTRACE_STAT_REG, tmp);
246 if (res != ERROR_OK)
247 return res;
248 xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
249 res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
250 if (res != ERROR_OK) {
251 LOG_ERROR("Failed to exec JTAG queue!");
252 return res;
253 }
254 *stat = buf_get_u32(tmp, 0, 32);
255 return ERROR_OK;
256 }
257
258 int esp_xtensa_apptrace_status_reg_write(struct target *target, uint32_t stat)
259 {
260 struct xtensa *xtensa = target_to_xtensa(target);
261
262 xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_STAT_REG, stat);
263 xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
264 int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
265 if (res != ERROR_OK) {
266 LOG_ERROR("Failed to exec JTAG queue!");
267 return res;
268 }
269 return ERROR_OK;
270 }
271
272 static int esp_xtensa_swdbg_activate(struct target *target, int enab)
273 {
274 struct xtensa *xtensa = target_to_xtensa(target);
275
276 xtensa_queue_dbg_reg_write(xtensa, enab ? XDMREG_DCRSET : XDMREG_DCRCLR, OCDDCR_DEBUGSWACTIVE);
277 xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
278 int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
279 if (res != ERROR_OK) {
280 LOG_ERROR("%s: writing DCR failed!", target->cmd_name);
281 return res;
282 }
283
284 return ERROR_OK;
285 }
286
287 static int esp_xtensa_apptrace_leave_crit_section_start(struct target *target)
288 {
289 /* TODO: not sure that we need this, but it seems that we fail to leave tracing critical
290 *section w/o this */
291 int res = esp_xtensa_swdbg_activate(target, 1 /*enable*/);
292 if (res != ERROR_OK) {
293 LOG_ERROR("Failed to activate SW debug (%d)!", res);
294 return res;
295 }
296 return ERROR_OK;
297 }
298
299 static int esp_xtensa_apptrace_leave_crit_section_stop(struct target *target)
300 {
301 int res = esp_xtensa_swdbg_activate(target, 0 /*disable*/);
302 if (res != ERROR_OK) {
303 LOG_ERROR("Failed to activate SW debug (%d)!", res);
304 return res;
305 }
306 return ERROR_OK;
307 }
308
309 static int esp_xtensa_apptrace_queue_reverse_write(struct target *target, uint32_t bufs_num,
310 uint32_t buf_sz[], const uint8_t *bufs[])
311 {
312 int res = ERROR_OK;
313 uint32_t cached_bytes = 0, total_sz = 0;
314 uint8_t cached_data8[sizeof(uint32_t)] = { 0 };
315 uint32_t cached_data32 = 0;
316
317 struct xtensa *xtensa = target_to_xtensa(target);
318
319 for (uint32_t i = 0; i < bufs_num; i++)
320 total_sz += buf_sz[i];
321 if (!IS_ALIGNED(total_sz, 4)) {
322 cached_bytes = sizeof(uint32_t) - (total_sz & 0x3UL);
323 total_sz = ALIGN_UP(total_sz, 4);
324 }
325 xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXADDR, (xtensa->core_config->trace.mem_sz - total_sz) / 4);
326 for (uint32_t i = bufs_num; i > 0; i--) {
327 uint32_t bsz = buf_sz[i - 1];
328 const uint8_t *cur_buf = &bufs[i - 1][bsz];
329 uint32_t bytes_to_cache;
330 /* if there are cached bytes from the previous buffer, combine them with the last
331 * from the current buffer */
332 if (cached_bytes) {
333 if ((cached_bytes + bsz) < sizeof(uint32_t))
334 bytes_to_cache = bsz;
335 else
336 bytes_to_cache = sizeof(uint32_t) - cached_bytes;
337 memcpy(&cached_data8[sizeof(uint32_t) - cached_bytes - bytes_to_cache],
338 cur_buf - bytes_to_cache,
339 bytes_to_cache);
340 cached_data32 = target_buffer_get_u32(target, cached_data8);
341 cached_bytes += bytes_to_cache;
342 if (cached_bytes < sizeof(uint32_t))
343 continue;
344 res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
345 if (res != ERROR_OK)
346 return res;
347 bsz -= bytes_to_cache;
348 cur_buf -= bytes_to_cache;
349 memset(cached_data8, 0x00, sizeof(cached_data8));
350 cached_bytes = 0;
351 }
352 /* write full dwords */
353 for (unsigned int k = bsz; k >= sizeof(uint32_t); k -= sizeof(uint32_t)) {
354 uint32_t temp = target_buffer_get_u32(target, cur_buf - sizeof(uint32_t));
355 res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, temp);
356 if (res != ERROR_OK)
357 return res;
358 cur_buf -= sizeof(uint32_t);
359 }
360 /* if there are bytes to be cached (1..3) */
361 bytes_to_cache = bsz & 0x3UL;
362 if (bytes_to_cache > 0) {
363 if (bytes_to_cache + cached_bytes >= sizeof(uint32_t)) {
364 /* filling the cache buffer from the end to beginning */
365 uint32_t to_copy = sizeof(uint32_t) - cached_bytes;
366 memcpy(&cached_data8[0], cur_buf - to_copy, to_copy);
367 cached_data32 = target_buffer_get_u32(target, cached_data8);
368 /* write full word of cached bytes */
369 res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
370 if (res != ERROR_OK)
371 return res;
372 /* cache remaining bytes */
373 memset(cached_data8, 0x00, sizeof(cached_data8));
374 cur_buf -= to_copy;
375 to_copy = bytes_to_cache + cached_bytes - sizeof(uint32_t);
376 memcpy(&cached_data8[sizeof(uint32_t) - to_copy], cur_buf - to_copy, to_copy);
377 cached_bytes = to_copy;
378 } else {
379 /* filling the cache buffer from the end to beginning */
380 memcpy(&cached_data8[sizeof(uint32_t) - cached_bytes - bytes_to_cache],
381 cur_buf - bytes_to_cache,
382 bytes_to_cache);
383 cached_bytes += bytes_to_cache;
384 }
385 }
386 }
387 return ERROR_OK;
388 }
389
390 static int esp_xtensa_apptrace_queue_normal_write(struct target *target, uint32_t bufs_num,
391 uint32_t buf_sz[], const uint8_t *bufs[])
392 {
393 int res = ERROR_OK;
394 uint32_t cached_bytes = 0;
395 uint8_t cached_data8[4] = { 0 };
396 uint32_t cached_data32 = 0;
397
398 struct xtensa *xtensa = target_to_xtensa(target);
399
400 /* | 1 | 2 | 1 | 2 | 4 |.......|
401 * | 4 | 4 | 4 | */
402 xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXADDR, 0);
403 for (unsigned int i = 0; i < bufs_num; i++) {
404 uint32_t bsz = buf_sz[i];
405 const uint8_t *cur_buf = bufs[i];
406 uint32_t bytes_to_cache;
407 /* if there are cached bytes from the previous buffer, combine them with the last
408 * from the current buffer */
409 if (cached_bytes) {
410 if ((cached_bytes + bsz) < sizeof(uint32_t))
411 bytes_to_cache = bsz;
412 else
413 bytes_to_cache = sizeof(uint32_t) - cached_bytes;
414 memcpy(&cached_data8[cached_bytes], cur_buf, bytes_to_cache);
415 cached_bytes += bytes_to_cache;
416 if (cached_bytes < sizeof(uint32_t))
417 continue;
418 cached_data32 = target_buffer_get_u32(target, cached_data8);
419 res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
420 if (res != ERROR_OK)
421 return res;
422 bsz -= bytes_to_cache;
423 cur_buf += bytes_to_cache;
424 memset(cached_data8, 0x00, sizeof(cached_data8));
425 cached_bytes = 0;
426 }
427 /* write full dwords */
428 for (unsigned int k = 0; (k + sizeof(uint32_t)) <= bsz; k += sizeof(uint32_t)) {
429 uint32_t temp = target_buffer_get_u32(target, cur_buf);
430 res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, temp);
431 if (res != ERROR_OK)
432 return res;
433 cur_buf += sizeof(uint32_t);
434 }
435 /* if there are bytes to be cached (1..3) */
436 bytes_to_cache = bsz & 0x3UL;
437 if (bytes_to_cache > 0) {
438 if (bytes_to_cache + cached_bytes >= sizeof(uint32_t)) {
439 memcpy(&cached_data8[0], cur_buf, sizeof(uint32_t) - cached_bytes);
440 cached_data32 = target_buffer_get_u32(target, cached_data8);
441 /* write full word of cached bytes */
442 res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
443 if (res != ERROR_OK)
444 return res;
445 /* cache remaining bytes */
446 memset(cached_data8, 0x00, sizeof(cached_data8));
447 cur_buf += sizeof(uint32_t) - cached_bytes;
448 cached_bytes = bytes_to_cache + cached_bytes - sizeof(uint32_t);
449 memcpy(&cached_data8[0], cur_buf, cached_bytes);
450 } else {
451 memcpy(&cached_data8[cached_bytes], cur_buf, bytes_to_cache);
452 cached_bytes += bytes_to_cache;
453 }
454 }
455 }
456 if (cached_bytes) {
457 /* write remaining cached bytes */
458 cached_data32 = target_buffer_get_u32(target, cached_data8);
459 res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
460 if (res != ERROR_OK)
461 return res;
462 }
463 return ERROR_OK;
464 }
465
466 static int esp_xtensa_apptrace_buffs_write(struct target *target,
467 uint32_t bufs_num,
468 uint32_t buf_sz[],
469 const uint8_t *bufs[],
470 uint32_t block_id,
471 bool ack,
472 bool data)
473 {
474 struct xtensa *xtensa = target_to_xtensa(target);
475 int res = ERROR_OK;
476 uint32_t tmp = XTENSA_APPTRACE_HOST_CONNECT |
477 (data ? XTENSA_APPTRACE_HOST_DATA : 0) | XTENSA_APPTRACE_BLOCK_ID(block_id) |
478 XTENSA_APPTRACE_BLOCK_LEN(0);
479
480 if (xtensa->core_config->trace.reversed_mem_access)
481 res = esp_xtensa_apptrace_queue_reverse_write(target, bufs_num, buf_sz, bufs);
482 else
483 res = esp_xtensa_apptrace_queue_normal_write(target, bufs_num, buf_sz, bufs);
484 if (res != ERROR_OK)
485 return res;
486 if (ack) {
487 LOG_DEBUG("Ack block %" PRId32 " on target (%s)!", block_id, target_name(target));
488 res = xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
489 if (res != ERROR_OK)
490 return res;
491 }
492 xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
493 res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
494 if (res != ERROR_OK) {
495 LOG_ERROR("Failed to exec JTAG queue!");
496 return res;
497 }
498 return ERROR_OK;
499 }

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)