target/riscv-011: Fix memory leak in handle_halt_routine()
[openocd.git] / src / target / armv7a.c
1 /***************************************************************************
2 * Copyright (C) 2009 by David Brownell *
3 * *
4 * Copyright (C) ST-Ericsson SA 2011 michel.jaouen@stericsson.com *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 ***************************************************************************/
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <helper/replacements.h>
25
26 #include "armv7a.h"
27 #include "armv7a_mmu.h"
28 #include "arm_disassembler.h"
29
30 #include "register.h"
31 #include <helper/binarybuffer.h>
32 #include <helper/command.h>
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include "arm_opcodes.h"
39 #include "target.h"
40 #include "target_type.h"
41
42 static void armv7a_show_fault_registers(struct target *target)
43 {
44 uint32_t dfsr, ifsr, dfar, ifar;
45 struct armv7a_common *armv7a = target_to_armv7a(target);
46 struct arm_dpm *dpm = armv7a->arm.dpm;
47 int retval;
48
49 retval = dpm->prepare(dpm);
50 if (retval != ERROR_OK)
51 return;
52
53 /* ARMV4_5_MRC(cpnum, op1, r0, CRn, CRm, op2) */
54
55 /* c5/c0 - {data, instruction} fault status registers */
56 retval = dpm->instr_read_data_r0(dpm,
57 ARMV4_5_MRC(15, 0, 0, 5, 0, 0),
58 &dfsr);
59 if (retval != ERROR_OK)
60 goto done;
61
62 retval = dpm->instr_read_data_r0(dpm,
63 ARMV4_5_MRC(15, 0, 0, 5, 0, 1),
64 &ifsr);
65 if (retval != ERROR_OK)
66 goto done;
67
68 /* c6/c0 - {data, instruction} fault address registers */
69 retval = dpm->instr_read_data_r0(dpm,
70 ARMV4_5_MRC(15, 0, 0, 6, 0, 0),
71 &dfar);
72 if (retval != ERROR_OK)
73 goto done;
74
75 retval = dpm->instr_read_data_r0(dpm,
76 ARMV4_5_MRC(15, 0, 0, 6, 0, 2),
77 &ifar);
78 if (retval != ERROR_OK)
79 goto done;
80
81 LOG_USER("Data fault registers DFSR: %8.8" PRIx32
82 ", DFAR: %8.8" PRIx32, dfsr, dfar);
83 LOG_USER("Instruction fault registers IFSR: %8.8" PRIx32
84 ", IFAR: %8.8" PRIx32, ifsr, ifar);
85
86 done:
87 /* (void) */ dpm->finish(dpm);
88 }
89
90
91 /* retrieve main id register */
92 static int armv7a_read_midr(struct target *target)
93 {
94 int retval = ERROR_FAIL;
95 struct armv7a_common *armv7a = target_to_armv7a(target);
96 struct arm_dpm *dpm = armv7a->arm.dpm;
97 uint32_t midr;
98 retval = dpm->prepare(dpm);
99 if (retval != ERROR_OK)
100 goto done;
101 /* MRC p15,0,<Rd>,c0,c0,0; read main id register*/
102
103 retval = dpm->instr_read_data_r0(dpm,
104 ARMV4_5_MRC(15, 0, 0, 0, 0, 0),
105 &midr);
106 if (retval != ERROR_OK)
107 goto done;
108
109 armv7a->rev = (midr & 0xf);
110 armv7a->partnum = (midr >> 4) & 0xfff;
111 armv7a->arch = (midr >> 16) & 0xf;
112 armv7a->variant = (midr >> 20) & 0xf;
113 armv7a->implementor = (midr >> 24) & 0xff;
114 LOG_INFO("%s rev %" PRIx32 ", partnum %" PRIx32 ", arch %" PRIx32
115 ", variant %" PRIx32 ", implementor %" PRIx32,
116 target->cmd_name,
117 armv7a->rev,
118 armv7a->partnum,
119 armv7a->arch,
120 armv7a->variant,
121 armv7a->implementor);
122
123 done:
124 dpm->finish(dpm);
125 return retval;
126 }
127
128 int armv7a_read_ttbcr(struct target *target)
129 {
130 struct armv7a_common *armv7a = target_to_armv7a(target);
131 struct arm_dpm *dpm = armv7a->arm.dpm;
132 uint32_t ttbcr, ttbcr_n;
133 int ttbidx;
134 int retval;
135
136 retval = dpm->prepare(dpm);
137 if (retval != ERROR_OK)
138 goto done;
139
140 /* MRC p15,0,<Rt>,c2,c0,2 ; Read CP15 Translation Table Base Control Register*/
141 retval = dpm->instr_read_data_r0(dpm,
142 ARMV4_5_MRC(15, 0, 0, 2, 0, 2),
143 &ttbcr);
144 if (retval != ERROR_OK)
145 goto done;
146
147 LOG_DEBUG("ttbcr %" PRIx32, ttbcr);
148
149 ttbcr_n = ttbcr & 0x7;
150 armv7a->armv7a_mmu.ttbcr = ttbcr;
151 armv7a->armv7a_mmu.cached = 1;
152
153 for (ttbidx = 0; ttbidx < 2; ttbidx++) {
154 /* MRC p15,0,<Rt>,c2,c0,ttbidx */
155 retval = dpm->instr_read_data_r0(dpm,
156 ARMV4_5_MRC(15, 0, 0, 2, 0, ttbidx),
157 &armv7a->armv7a_mmu.ttbr[ttbidx]);
158 if (retval != ERROR_OK)
159 goto done;
160 }
161
162 /*
163 * ARM Architecture Reference Manual (ARMv7-A and ARMv7-Redition),
164 * document # ARM DDI 0406C
165 */
166 armv7a->armv7a_mmu.ttbr_range[0] = 0xffffffff >> ttbcr_n;
167 armv7a->armv7a_mmu.ttbr_range[1] = 0xffffffff;
168 armv7a->armv7a_mmu.ttbr_mask[0] = 0xffffffff << (14 - ttbcr_n);
169 armv7a->armv7a_mmu.ttbr_mask[1] = 0xffffffff << 14;
170 armv7a->armv7a_mmu.cached = 1;
171
172 retval = armv7a_read_midr(target);
173 if (retval != ERROR_OK)
174 goto done;
175
176 /* FIXME: why this special case based on part number? */
177 if ((armv7a->partnum & 0xf) == 0) {
178 /* ARM DDI 0344H , ARM DDI 0407F */
179 armv7a->armv7a_mmu.ttbr_mask[0] = 7 << (32 - ttbcr_n);
180 }
181
182 LOG_DEBUG("ttbr1 %s, ttbr0_mask %" PRIx32 " ttbr1_mask %" PRIx32,
183 (ttbcr_n != 0) ? "used" : "not used",
184 armv7a->armv7a_mmu.ttbr_mask[0],
185 armv7a->armv7a_mmu.ttbr_mask[1]);
186
187 done:
188 dpm->finish(dpm);
189 return retval;
190 }
191
192 /* FIXME: remove it */
193 static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t way)
194 {
195 struct armv7a_l2x_cache *l2x_cache;
196 struct target_list *head = target->head;
197 struct target *curr;
198
199 struct armv7a_common *armv7a = target_to_armv7a(target);
200 l2x_cache = calloc(1, sizeof(struct armv7a_l2x_cache));
201 l2x_cache->base = base;
202 l2x_cache->way = way;
203 /*LOG_INFO("cache l2 initialized base %x way %d",
204 l2x_cache->base,l2x_cache->way);*/
205 if (armv7a->armv7a_mmu.armv7a_cache.outer_cache)
206 LOG_INFO("outer cache already initialized\n");
207 armv7a->armv7a_mmu.armv7a_cache.outer_cache = l2x_cache;
208 /* initialize all target in this cluster (smp target)
209 * l2 cache must be configured after smp declaration */
210 while (head != (struct target_list *)NULL) {
211 curr = head->target;
212 if (curr != target) {
213 armv7a = target_to_armv7a(curr);
214 if (armv7a->armv7a_mmu.armv7a_cache.outer_cache)
215 LOG_ERROR("smp target : outer cache already initialized\n");
216 armv7a->armv7a_mmu.armv7a_cache.outer_cache = l2x_cache;
217 }
218 head = head->next;
219 }
220 return JIM_OK;
221 }
222
223 /* FIXME: remove it */
224 COMMAND_HANDLER(handle_cache_l2x)
225 {
226 struct target *target = get_current_target(CMD_CTX);
227 uint32_t base, way;
228
229 if (CMD_ARGC != 2)
230 return ERROR_COMMAND_SYNTAX_ERROR;
231
232 /* command_print(CMD_CTX, "%s %s", CMD_ARGV[0], CMD_ARGV[1]); */
233 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], base);
234 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], way);
235
236 /* AP address is in bits 31:24 of DP_SELECT */
237 armv7a_l2x_cache_init(target, base, way);
238
239 return ERROR_OK;
240 }
241
242 int armv7a_handle_cache_info_command(struct command_context *cmd_ctx,
243 struct armv7a_cache_common *armv7a_cache)
244 {
245 struct armv7a_l2x_cache *l2x_cache = (struct armv7a_l2x_cache *)
246 (armv7a_cache->outer_cache);
247
248 int cl;
249
250 if (armv7a_cache->info == -1) {
251 command_print(cmd_ctx, "cache not yet identified");
252 return ERROR_OK;
253 }
254
255 for (cl = 0; cl < armv7a_cache->loc; cl++) {
256 struct armv7a_arch_cache *arch = &(armv7a_cache->arch[cl]);
257
258 if (arch->ctype & 1) {
259 command_print(cmd_ctx,
260 "L%d I-Cache: linelen %" PRIi32
261 ", associativity %" PRIi32
262 ", nsets %" PRIi32
263 ", cachesize %" PRId32 " KBytes",
264 cl+1,
265 arch->i_size.linelen,
266 arch->i_size.associativity,
267 arch->i_size.nsets,
268 arch->i_size.cachesize);
269 }
270
271 if (arch->ctype >= 2) {
272 command_print(cmd_ctx,
273 "L%d D-Cache: linelen %" PRIi32
274 ", associativity %" PRIi32
275 ", nsets %" PRIi32
276 ", cachesize %" PRId32 " KBytes",
277 cl+1,
278 arch->d_u_size.linelen,
279 arch->d_u_size.associativity,
280 arch->d_u_size.nsets,
281 arch->d_u_size.cachesize);
282 }
283 }
284
285 if (l2x_cache != NULL)
286 command_print(cmd_ctx, "Outer unified cache Base Address 0x%" PRIx32 ", %" PRId32 " ways",
287 l2x_cache->base, l2x_cache->way);
288
289 return ERROR_OK;
290 }
291
292 /* retrieve core id cluster id */
293 static int armv7a_read_mpidr(struct target *target)
294 {
295 int retval = ERROR_FAIL;
296 struct armv7a_common *armv7a = target_to_armv7a(target);
297 struct arm_dpm *dpm = armv7a->arm.dpm;
298 uint32_t mpidr;
299 retval = dpm->prepare(dpm);
300 if (retval != ERROR_OK)
301 goto done;
302 /* MRC p15,0,<Rd>,c0,c0,5; read Multiprocessor ID register*/
303
304 retval = dpm->instr_read_data_r0(dpm,
305 ARMV4_5_MRC(15, 0, 0, 0, 0, 5),
306 &mpidr);
307 if (retval != ERROR_OK)
308 goto done;
309
310 /* ARMv7R uses a different format for MPIDR.
311 * When configured uniprocessor (most R cores) it reads as 0.
312 * This will need to be implemented for multiprocessor ARMv7R cores. */
313 if (armv7a->is_armv7r) {
314 if (mpidr)
315 LOG_ERROR("MPIDR nonzero in ARMv7-R target");
316 goto done;
317 }
318
319 if (mpidr & 1<<31) {
320 armv7a->multi_processor_system = (mpidr >> 30) & 1;
321 armv7a->cluster_id = (mpidr >> 8) & 0xf;
322 armv7a->cpu_id = mpidr & 0x3;
323 LOG_INFO("%s cluster %x core %x %s", target_name(target),
324 armv7a->cluster_id,
325 armv7a->cpu_id,
326 armv7a->multi_processor_system == 0 ? "multi core" : "mono core");
327
328 } else
329 LOG_ERROR("MPIDR not in multiprocessor format");
330
331 done:
332 dpm->finish(dpm);
333 return retval;
334
335
336 }
337
338 static int get_cache_info(struct arm_dpm *dpm, int cl, int ct, uint32_t *cache_reg)
339 {
340 int retval = ERROR_OK;
341
342 /* select cache level */
343 retval = dpm->instr_write_data_r0(dpm,
344 ARMV4_5_MCR(15, 2, 0, 0, 0, 0),
345 (cl << 1) | (ct == 1 ? 1 : 0));
346 if (retval != ERROR_OK)
347 goto done;
348
349 retval = dpm->instr_read_data_r0(dpm,
350 ARMV4_5_MRC(15, 1, 0, 0, 0, 0),
351 cache_reg);
352 done:
353 return retval;
354 }
355
356 static struct armv7a_cachesize decode_cache_reg(uint32_t cache_reg)
357 {
358 struct armv7a_cachesize size;
359 int i = 0;
360
361 size.linelen = 16 << (cache_reg & 0x7);
362 size.associativity = ((cache_reg >> 3) & 0x3ff) + 1;
363 size.nsets = ((cache_reg >> 13) & 0x7fff) + 1;
364 size.cachesize = size.linelen * size.associativity * size.nsets / 1024;
365
366 /* compute info for set way operation on cache */
367 size.index_shift = (cache_reg & 0x7) + 4;
368 size.index = (cache_reg >> 13) & 0x7fff;
369 size.way = ((cache_reg >> 3) & 0x3ff);
370
371 while (((size.way << i) & 0x80000000) == 0)
372 i++;
373 size.way_shift = i;
374
375 return size;
376 }
377
378 int armv7a_identify_cache(struct target *target)
379 {
380 /* read cache descriptor */
381 int retval = ERROR_FAIL;
382 struct armv7a_common *armv7a = target_to_armv7a(target);
383 struct arm_dpm *dpm = armv7a->arm.dpm;
384 uint32_t csselr, clidr, ctr;
385 uint32_t cache_reg;
386 int cl, ctype;
387 struct armv7a_cache_common *cache =
388 &(armv7a->armv7a_mmu.armv7a_cache);
389
390 retval = dpm->prepare(dpm);
391 if (retval != ERROR_OK)
392 goto done;
393
394 /* retrieve CTR
395 * mrc p15, 0, r0, c0, c0, 1 @ read ctr */
396 retval = dpm->instr_read_data_r0(dpm,
397 ARMV4_5_MRC(15, 0, 0, 0, 0, 1),
398 &ctr);
399 if (retval != ERROR_OK)
400 goto done;
401
402 cache->iminline = 4UL << (ctr & 0xf);
403 cache->dminline = 4UL << ((ctr & 0xf0000) >> 16);
404 LOG_DEBUG("ctr %" PRIx32 " ctr.iminline %" PRId32 " ctr.dminline %" PRId32,
405 ctr, cache->iminline, cache->dminline);
406
407 /* retrieve CLIDR
408 * mrc p15, 1, r0, c0, c0, 1 @ read clidr */
409 retval = dpm->instr_read_data_r0(dpm,
410 ARMV4_5_MRC(15, 1, 0, 0, 0, 1),
411 &clidr);
412 if (retval != ERROR_OK)
413 goto done;
414
415 cache->loc = (clidr & 0x7000000) >> 24;
416 LOG_DEBUG("Number of cache levels to PoC %" PRId32, cache->loc);
417
418 /* retrieve selected cache for later restore
419 * MRC p15, 2,<Rd>, c0, c0, 0; Read CSSELR */
420 retval = dpm->instr_read_data_r0(dpm,
421 ARMV4_5_MRC(15, 2, 0, 0, 0, 0),
422 &csselr);
423 if (retval != ERROR_OK)
424 goto done;
425
426 /* retrieve all available inner caches */
427 for (cl = 0; cl < cache->loc; clidr >>= 3, cl++) {
428
429 /* isolate cache type at current level */
430 ctype = clidr & 7;
431
432 /* skip reserved values */
433 if (ctype > CACHE_LEVEL_HAS_UNIFIED_CACHE)
434 continue;
435
436 /* separate d or unified d/i cache at this level ? */
437 if (ctype & (CACHE_LEVEL_HAS_UNIFIED_CACHE | CACHE_LEVEL_HAS_D_CACHE)) {
438 /* retrieve d-cache info */
439 retval = get_cache_info(dpm, cl, 0, &cache_reg);
440 if (retval != ERROR_OK)
441 goto done;
442 cache->arch[cl].d_u_size = decode_cache_reg(cache_reg);
443
444 LOG_DEBUG("data/unified cache index %d << %d, way %d << %d",
445 cache->arch[cl].d_u_size.index,
446 cache->arch[cl].d_u_size.index_shift,
447 cache->arch[cl].d_u_size.way,
448 cache->arch[cl].d_u_size.way_shift);
449
450 LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
451 cache->arch[cl].d_u_size.linelen,
452 cache->arch[cl].d_u_size.cachesize,
453 cache->arch[cl].d_u_size.associativity);
454 }
455
456 /* separate i-cache at this level ? */
457 if (ctype & CACHE_LEVEL_HAS_I_CACHE) {
458 /* retrieve i-cache info */
459 retval = get_cache_info(dpm, cl, 1, &cache_reg);
460 if (retval != ERROR_OK)
461 goto done;
462 cache->arch[cl].i_size = decode_cache_reg(cache_reg);
463
464 LOG_DEBUG("instruction cache index %d << %d, way %d << %d",
465 cache->arch[cl].i_size.index,
466 cache->arch[cl].i_size.index_shift,
467 cache->arch[cl].i_size.way,
468 cache->arch[cl].i_size.way_shift);
469
470 LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
471 cache->arch[cl].i_size.linelen,
472 cache->arch[cl].i_size.cachesize,
473 cache->arch[cl].i_size.associativity);
474 }
475
476 cache->arch[cl].ctype = ctype;
477 }
478
479 /* restore selected cache */
480 dpm->instr_write_data_r0(dpm,
481 ARMV4_5_MRC(15, 2, 0, 0, 0, 0),
482 csselr);
483
484 if (retval != ERROR_OK)
485 goto done;
486
487 /* if no l2 cache initialize l1 data cache flush function function */
488 if (armv7a->armv7a_mmu.armv7a_cache.flush_all_data_cache == NULL) {
489 armv7a->armv7a_mmu.armv7a_cache.flush_all_data_cache =
490 armv7a_cache_auto_flush_all_data;
491 }
492
493 armv7a->armv7a_mmu.armv7a_cache.info = 1;
494 done:
495 dpm->finish(dpm);
496 armv7a_read_mpidr(target);
497 return retval;
498
499 }
500
501 static int armv7a_setup_semihosting(struct target *target, int enable)
502 {
503 struct armv7a_common *armv7a = target_to_armv7a(target);
504 uint32_t vcr;
505 int ret;
506
507 ret = mem_ap_read_atomic_u32(armv7a->debug_ap,
508 armv7a->debug_base + CPUDBG_VCR,
509 &vcr);
510 if (ret < 0) {
511 LOG_ERROR("Failed to read VCR register\n");
512 return ret;
513 }
514
515 if (enable)
516 vcr |= DBG_VCR_SVC_MASK;
517 else
518 vcr &= ~DBG_VCR_SVC_MASK;
519
520 ret = mem_ap_write_atomic_u32(armv7a->debug_ap,
521 armv7a->debug_base + CPUDBG_VCR,
522 vcr);
523 if (ret < 0)
524 LOG_ERROR("Failed to write VCR register\n");
525
526 return ret;
527 }
528
529 int armv7a_init_arch_info(struct target *target, struct armv7a_common *armv7a)
530 {
531 struct arm *arm = &armv7a->arm;
532 arm->arch_info = armv7a;
533 target->arch_info = &armv7a->arm;
534 arm->setup_semihosting = armv7a_setup_semihosting;
535 /* target is useful in all function arm v4 5 compatible */
536 armv7a->arm.target = target;
537 armv7a->arm.common_magic = ARM_COMMON_MAGIC;
538 armv7a->common_magic = ARMV7_COMMON_MAGIC;
539 armv7a->armv7a_mmu.armv7a_cache.info = -1;
540 armv7a->armv7a_mmu.armv7a_cache.outer_cache = NULL;
541 armv7a->armv7a_mmu.armv7a_cache.flush_all_data_cache = NULL;
542 armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled = 1;
543 return ERROR_OK;
544 }
545
546 int armv7a_arch_state(struct target *target)
547 {
548 static const char *state[] = {
549 "disabled", "enabled"
550 };
551
552 struct armv7a_common *armv7a = target_to_armv7a(target);
553 struct arm *arm = &armv7a->arm;
554
555 if (armv7a->common_magic != ARMV7_COMMON_MAGIC) {
556 LOG_ERROR("BUG: called for a non-ARMv7A target");
557 return ERROR_COMMAND_SYNTAX_ERROR;
558 }
559
560 arm_arch_state(target);
561
562 if (armv7a->is_armv7r) {
563 LOG_USER("D-Cache: %s, I-Cache: %s",
564 state[armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled],
565 state[armv7a->armv7a_mmu.armv7a_cache.i_cache_enabled]);
566 } else {
567 LOG_USER("MMU: %s, D-Cache: %s, I-Cache: %s",
568 state[armv7a->armv7a_mmu.mmu_enabled],
569 state[armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled],
570 state[armv7a->armv7a_mmu.armv7a_cache.i_cache_enabled]);
571 }
572
573 if (arm->core_mode == ARM_MODE_ABT)
574 armv7a_show_fault_registers(target);
575 if (target->debug_reason == DBG_REASON_WATCHPOINT)
576 LOG_USER("Watchpoint triggered at PC %#08x",
577 (unsigned) armv7a->dpm.wp_pc);
578
579 return ERROR_OK;
580 }
581
582 static const struct command_registration l2_cache_commands[] = {
583 {
584 .name = "l2x",
585 .handler = handle_cache_l2x,
586 .mode = COMMAND_EXEC,
587 .help = "configure l2x cache "
588 "",
589 .usage = "[base_addr] [number_of_way]",
590 },
591 COMMAND_REGISTRATION_DONE
592
593 };
594
595 const struct command_registration l2x_cache_command_handlers[] = {
596 {
597 .name = "cache_config",
598 .mode = COMMAND_EXEC,
599 .help = "cache configuration for a target",
600 .usage = "",
601 .chain = l2_cache_commands,
602 },
603 COMMAND_REGISTRATION_DONE
604 };
605
606 const struct command_registration armv7a_command_handlers[] = {
607 {
608 .chain = l2x_cache_command_handlers,
609 },
610 {
611 .chain = arm7a_cache_command_handlers,
612 },
613 COMMAND_REGISTRATION_DONE
614 };

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)