Fix "unused variable" warnings (errors) detected with GCC 4.7.0 - trivial fixes
[openocd.git] / src / flash / nor / core.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath <Dominic.Rath@gmx.de> *
3 * Copyright (C) 2007-2010 Øyvind Harboe <oyvind.harboe@zylin.com> *
4 * Copyright (C) 2008 by Spencer Oliver <spen@spen-soft.co.uk> *
5 * Copyright (C) 2009 Zachary T Welch <zw@superlucidity.net> *
6 * Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com> *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 #include <flash/common.h>
28 #include <flash/nor/core.h>
29 #include <flash/nor/imp.h>
30 #include <target/image.h>
31
32
33 /**
34 * @file
35 * Upper level of NOR flash framework.
36 * The lower level interfaces are to drivers. These upper level ones
37 * primarily support access from Tcl scripts or from GDB.
38 */
39
40 static struct flash_bank *flash_banks;
41
42 int flash_driver_erase(struct flash_bank *bank, int first, int last)
43 {
44 int retval;
45
46 retval = bank->driver->erase(bank, first, last);
47 if (retval != ERROR_OK)
48 {
49 LOG_ERROR("failed erasing sectors %d to %d", first, last);
50 }
51
52 return retval;
53 }
54
55 int flash_driver_protect(struct flash_bank *bank, int set, int first, int last)
56 {
57 int retval;
58
59 /* callers may not supply illegal parameters ... */
60 if (first < 0 || first > last || last >= bank->num_sectors)
61 {
62 LOG_ERROR("illegal sector range");
63 return ERROR_FAIL;
64 }
65
66 /* force "set" to 0/1 */
67 set = !!set;
68
69 /* DANGER!
70 *
71 * We must not use any cached information about protection state!!!!
72 *
73 * There are a million things that could change the protect state:
74 *
75 * the target could have reset, power cycled, been hot plugged,
76 * the application could have run, etc.
77 *
78 * Drivers only receive valid sector range.
79 */
80 retval = bank->driver->protect(bank, set, first, last);
81 if (retval != ERROR_OK)
82 {
83 LOG_ERROR("failed setting protection for areas %d to %d", first, last);
84 }
85
86 return retval;
87 }
88
89 int flash_driver_write(struct flash_bank *bank,
90 uint8_t *buffer, uint32_t offset, uint32_t count)
91 {
92 int retval;
93
94 retval = bank->driver->write(bank, buffer, offset, count);
95 if (retval != ERROR_OK)
96 {
97 LOG_ERROR("error writing to flash at address 0x%08" PRIx32 " at offset 0x%8.8" PRIx32,
98 bank->base, offset);
99 }
100
101 return retval;
102 }
103
104 int flash_driver_read(struct flash_bank *bank,
105 uint8_t *buffer, uint32_t offset, uint32_t count)
106 {
107 int retval;
108
109 LOG_DEBUG("call flash_driver_read()");
110
111 retval = bank->driver->read(bank, buffer, offset, count);
112 if (retval != ERROR_OK)
113 {
114 LOG_ERROR("error reading to flash at address 0x%08" PRIx32 " at offset 0x%8.8" PRIx32,
115 bank->base, offset);
116 }
117
118 return retval;
119 }
120
121 int default_flash_read(struct flash_bank *bank,
122 uint8_t *buffer, uint32_t offset, uint32_t count)
123 {
124 return target_read_buffer(bank->target, offset + bank->base, count, buffer);
125 }
126
127 void flash_bank_add(struct flash_bank *bank)
128 {
129 /* put flash bank in linked list */
130 unsigned bank_num = 0;
131 if (flash_banks)
132 {
133 /* find last flash bank */
134 struct flash_bank *p = flash_banks;
135 while (NULL != p->next)
136 {
137 bank_num += 1;
138 p = p->next;
139 }
140 p->next = bank;
141 bank_num += 1;
142 }
143 else
144 flash_banks = bank;
145
146 bank->bank_number = bank_num;
147 }
148
149 struct flash_bank *flash_bank_list(void)
150 {
151 return flash_banks;
152 }
153
154 struct flash_bank *get_flash_bank_by_num_noprobe(int num)
155 {
156 struct flash_bank *p;
157 int i = 0;
158
159 for (p = flash_banks; p; p = p->next)
160 {
161 if (i++ == num)
162 {
163 return p;
164 }
165 }
166 LOG_ERROR("flash bank %d does not exist", num);
167 return NULL;
168 }
169
170 int flash_get_bank_count(void)
171 {
172 struct flash_bank *p;
173 int i = 0;
174 for (p = flash_banks; p; p = p->next)
175 {
176 i++;
177 }
178 return i;
179 }
180
181 struct flash_bank *get_flash_bank_by_name_noprobe(const char *name)
182 {
183 unsigned requested = get_flash_name_index(name);
184 unsigned found = 0;
185
186 struct flash_bank *bank;
187 for (bank = flash_banks; NULL != bank; bank = bank->next)
188 {
189 if (strcmp(bank->name, name) == 0)
190 return bank;
191 if (!flash_driver_name_matches(bank->driver->name, name))
192 continue;
193 if (++found < requested)
194 continue;
195 return bank;
196 }
197 return NULL;
198 }
199
200 int get_flash_bank_by_name(const char *name, struct flash_bank **bank_result)
201 {
202 struct flash_bank *bank;
203 int retval;
204
205 bank = get_flash_bank_by_name_noprobe(name);
206 if (bank != NULL)
207 {
208 retval = bank->driver->auto_probe(bank);
209
210 if (retval != ERROR_OK)
211 {
212 LOG_ERROR("auto_probe failed");
213 return retval;
214 }
215 }
216
217 *bank_result = bank;
218 return ERROR_OK;
219 }
220
221 int get_flash_bank_by_num(int num, struct flash_bank **bank)
222 {
223 struct flash_bank *p = get_flash_bank_by_num_noprobe(num);
224 int retval;
225
226 if (p == NULL)
227 {
228 return ERROR_FAIL;
229 }
230
231 retval = p->driver->auto_probe(p);
232
233 if (retval != ERROR_OK)
234 {
235 LOG_ERROR("auto_probe failed");
236 return retval;
237 }
238 *bank = p;
239 return ERROR_OK;
240 }
241
242 /* lookup flash bank by address, bank not found is success, but
243 * result_bank is set to NULL. */
244 int get_flash_bank_by_addr(struct target *target, uint32_t addr, bool check, struct flash_bank **result_bank)
245 {
246 struct flash_bank *c;
247
248 /* cycle through bank list */
249 for (c = flash_banks; c; c = c->next)
250 {
251 int retval;
252 retval = c->driver->auto_probe(c);
253
254 if (retval != ERROR_OK)
255 {
256 LOG_ERROR("auto_probe failed");
257 return retval;
258 }
259 /* check whether address belongs to this flash bank */
260 if ((addr >= c->base) && (addr <= c->base + (c->size - 1)) && target == c->target)
261 {
262 *result_bank = c;
263 return ERROR_OK;
264 }
265 }
266 *result_bank = NULL;
267 if (check)
268 {
269 LOG_ERROR("No flash at address 0x%08" PRIx32, addr);
270 return ERROR_FAIL;
271 }
272 return ERROR_OK;
273 }
274
275 int default_flash_mem_blank_check(struct flash_bank *bank)
276 {
277 struct target *target = bank->target;
278 const int buffer_size = 1024;
279 int i;
280 uint32_t nBytes;
281 int retval = ERROR_OK;
282
283 if (bank->target->state != TARGET_HALTED)
284 {
285 LOG_ERROR("Target not halted");
286 return ERROR_TARGET_NOT_HALTED;
287 }
288
289 uint8_t *buffer = malloc(buffer_size);
290
291 for (i = 0; i < bank->num_sectors; i++)
292 {
293 uint32_t j;
294 bank->sectors[i].is_erased = 1;
295
296 for (j = 0; j < bank->sectors[i].size; j += buffer_size)
297 {
298 uint32_t chunk;
299 chunk = buffer_size;
300 if (chunk > (j - bank->sectors[i].size))
301 {
302 chunk = (j - bank->sectors[i].size);
303 }
304
305 retval = target_read_memory(target, bank->base + bank->sectors[i].offset + j, 4, chunk/4, buffer);
306 if (retval != ERROR_OK)
307 {
308 goto done;
309 }
310
311 for (nBytes = 0; nBytes < chunk; nBytes++)
312 {
313 if (buffer[nBytes] != 0xFF)
314 {
315 bank->sectors[i].is_erased = 0;
316 break;
317 }
318 }
319 }
320 }
321
322 done:
323 free(buffer);
324
325 return retval;
326 }
327
328 int default_flash_blank_check(struct flash_bank *bank)
329 {
330 struct target *target = bank->target;
331 int i;
332 int retval;
333 int fast_check = 0;
334 uint32_t blank;
335
336 if (bank->target->state != TARGET_HALTED)
337 {
338 LOG_ERROR("Target not halted");
339 return ERROR_TARGET_NOT_HALTED;
340 }
341
342 for (i = 0; i < bank->num_sectors; i++)
343 {
344 uint32_t address = bank->base + bank->sectors[i].offset;
345 uint32_t size = bank->sectors[i].size;
346
347 if ((retval = target_blank_check_memory(target, address, size, &blank)) != ERROR_OK)
348 {
349 fast_check = 0;
350 break;
351 }
352 if (blank == 0xFF)
353 bank->sectors[i].is_erased = 1;
354 else
355 bank->sectors[i].is_erased = 0;
356 fast_check = 1;
357 }
358
359 if (!fast_check)
360 {
361 LOG_USER("Running slow fallback erase check - add working memory");
362 return default_flash_mem_blank_check(bank);
363 }
364
365 return ERROR_OK;
366 }
367
368 /* Manipulate given flash region, selecting the bank according to target
369 * and address. Maps an address range to a set of sectors, and issues
370 * the callback() on that set ... e.g. to erase or unprotect its members.
371 *
372 * (Note a current bad assumption: that protection operates on the same
373 * size sectors as erase operations use.)
374 *
375 * The "pad_reason" parameter is a kind of boolean: when it's NULL, the
376 * range must fit those sectors exactly. This is clearly safe; it can't
377 * erase data which the caller said to leave alone, for example. If it's
378 * non-NULL, rather than failing, extra data in the first and/or last
379 * sectors will be added to the range, and that reason string is used when
380 * warning about those additions.
381 */
382 static int flash_iterate_address_range_inner(struct target *target,
383 char *pad_reason, uint32_t addr, uint32_t length,
384 int (*callback)(struct flash_bank *bank, int first, int last))
385 {
386 struct flash_bank *c;
387 uint32_t last_addr = addr + length; /* first address AFTER end */
388 int first = -1;
389 int last = -1;
390 int i;
391
392 int retval = get_flash_bank_by_addr(target, addr, true, &c);
393 if (retval != ERROR_OK)
394 return retval;
395
396 if (c->size == 0 || c->num_sectors == 0)
397 {
398 LOG_ERROR("Bank is invalid");
399 return ERROR_FLASH_BANK_INVALID;
400 }
401
402 if (length == 0)
403 {
404 /* special case, erase whole bank when length is zero */
405 if (addr != c->base)
406 {
407 LOG_ERROR("Whole bank access must start at beginning of bank.");
408 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
409 }
410
411 return callback(c, 0, c->num_sectors - 1);
412 }
413
414 /* check whether it all fits in this bank */
415 if (addr + length - 1 > c->base + c->size - 1)
416 {
417 LOG_ERROR("Flash access does not fit into bank.");
418 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
419 }
420
421 /** @todo: handle erasures that cross into adjacent banks */
422
423 addr -= c->base;
424 last_addr -= c->base;
425
426 for (i = 0; i < c->num_sectors; i++)
427 {
428 struct flash_sector *f = c->sectors + i;
429 uint32_t end = f->offset + f->size;
430
431 /* start only on a sector boundary */
432 if (first < 0) {
433 /* scanned past the first sector? */
434 if (addr < f->offset)
435 break;
436
437 /* is this the first sector? */
438 if (addr == f->offset)
439 first = i;
440
441 /* Does this need head-padding? If so, pad and warn;
442 * or else force an error.
443 *
444 * Such padding can make trouble, since *WE* can't
445 * ever know if that data was in use. The warning
446 * should help users sort out messes later.
447 */
448 else if (addr < end && pad_reason) {
449 /* FIXME say how many bytes (e.g. 80 KB) */
450 LOG_WARNING("Adding extra %s range, "
451 "%#8.8x to %#8.8x",
452 pad_reason,
453 (unsigned) f->offset,
454 (unsigned) addr - 1);
455 first = i;
456 } else
457 continue;
458 }
459
460 /* is this (also?) the last sector? */
461 if (last_addr == end) {
462 last = i;
463 break;
464 }
465
466 /* Does this need tail-padding? If so, pad and warn;
467 * or else force an error.
468 */
469 if (last_addr < end && pad_reason) {
470 /* FIXME say how many bytes (e.g. 80 KB) */
471 LOG_WARNING("Adding extra %s range, "
472 "%#8.8x to %#8.8x",
473 pad_reason,
474 (unsigned) last_addr,
475 (unsigned) end - 1);
476 last = i;
477 break;
478 }
479
480 /* MUST finish on a sector boundary */
481 if (last_addr <= f->offset)
482 break;
483 }
484
485 /* invalid start or end address? */
486 if (first == -1 || last == -1) {
487 LOG_ERROR("address range 0x%8.8x .. 0x%8.8x "
488 "is not sector-aligned",
489 (unsigned) (c->base + addr),
490 (unsigned) (c->base + last_addr - 1));
491 return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
492 }
493
494 /* The NOR driver may trim this range down, based on what
495 * sectors are already erased/unprotected. GDB currently
496 * blocks such optimizations.
497 */
498 return callback(c, first, last);
499 }
500
501 /* The inner fn only handles a single bank, we could be spanning
502 * multiple chips.
503 */
504 static int flash_iterate_address_range(struct target *target,
505 char *pad_reason, uint32_t addr, uint32_t length,
506 int (*callback)(struct flash_bank *bank, int first, int last))
507 {
508 struct flash_bank *c;
509 int retval = ERROR_OK;
510
511 /* Danger! zero-length iterations means entire bank! */
512 do
513 {
514 retval = get_flash_bank_by_addr(target, addr, true, &c);
515 if (retval != ERROR_OK)
516 return retval;
517
518 uint32_t cur_length = length;
519 /* check whether it all fits in this bank */
520 if (addr + length - 1 > c->base + c->size - 1)
521 {
522 LOG_DEBUG("iterating over more than one flash bank.");
523 cur_length = c->base + c->size - addr;
524 }
525 retval = flash_iterate_address_range_inner(target,
526 pad_reason, addr, cur_length,
527 callback);
528 if (retval != ERROR_OK)
529 break;
530
531 length -= cur_length;
532 addr += cur_length;
533 } while (length > 0);
534
535 return retval;
536 }
537
538 int flash_erase_address_range(struct target *target,
539 bool pad, uint32_t addr, uint32_t length)
540 {
541 return flash_iterate_address_range(target, pad ? "erase" : NULL,
542 addr, length, &flash_driver_erase);
543 }
544
545 static int flash_driver_unprotect(struct flash_bank *bank, int first, int last)
546 {
547 return flash_driver_protect(bank, 0, first, last);
548 }
549
550 int flash_unlock_address_range(struct target *target, uint32_t addr, uint32_t length)
551 {
552 /* By default, pad to sector boundaries ... the real issue here
553 * is that our (only) caller *permanently* removes protection,
554 * and doesn't restore it.
555 */
556 return flash_iterate_address_range(target, "unprotect",
557 addr, length, &flash_driver_unprotect);
558 }
559
560 static int compare_section (const void * a, const void * b)
561 {
562 struct imagesection *b1, *b2;
563 b1=*((struct imagesection **)a);
564 b2=*((struct imagesection **)b);
565
566 if (b1->base_address == b2->base_address)
567 {
568 return 0;
569 } else if (b1->base_address > b2->base_address)
570 {
571 return 1;
572 } else
573 {
574 return -1;
575 }
576 }
577
578
579 int flash_write_unlock(struct target *target, struct image *image,
580 uint32_t *written, int erase, bool unlock)
581 {
582 int retval = ERROR_OK;
583
584 int section;
585 uint32_t section_offset;
586 struct flash_bank *c;
587 int *padding;
588
589 section = 0;
590 section_offset = 0;
591
592 if (written)
593 *written = 0;
594
595 if (erase)
596 {
597 /* assume all sectors need erasing - stops any problems
598 * when flash_write is called multiple times */
599
600 flash_set_dirty();
601 }
602
603 /* allocate padding array */
604 padding = calloc(image->num_sections, sizeof(*padding));
605
606 /* This fn requires all sections to be in ascending order of addresses,
607 * whereas an image can have sections out of order. */
608 struct imagesection **sections = malloc(sizeof(struct imagesection *) *
609 image->num_sections);
610 int i;
611 for (i = 0; i < image->num_sections; i++)
612 {
613 sections[i] = &image->sections[i];
614 }
615
616 qsort(sections, image->num_sections, sizeof(struct imagesection *),
617 compare_section);
618
619 /* loop until we reach end of the image */
620 while (section < image->num_sections)
621 {
622 uint32_t buffer_size;
623 uint8_t *buffer;
624 int section_last;
625 uint32_t run_address = sections[section]->base_address + section_offset;
626 uint32_t run_size = sections[section]->size - section_offset;
627 int pad_bytes = 0;
628
629 if (sections[section]->size == 0)
630 {
631 LOG_WARNING("empty section %d", section);
632 section++;
633 section_offset = 0;
634 continue;
635 }
636
637 /* find the corresponding flash bank */
638 retval = get_flash_bank_by_addr(target, run_address, false, &c);
639 if (retval != ERROR_OK)
640 {
641 goto done;
642 }
643 if (c == NULL)
644 {
645 section++; /* and skip it */
646 section_offset = 0;
647 continue;
648 }
649
650 /* collect consecutive sections which fall into the same bank */
651 section_last = section;
652 padding[section] = 0;
653 while ((run_address + run_size - 1 < c->base + c->size - 1)
654 && (section_last + 1 < image->num_sections))
655 {
656 /* sections are sorted */
657 assert(sections[section_last + 1]->base_address >= c->base);
658 if (sections[section_last + 1]->base_address >= (c->base + c->size))
659 {
660 /* Done with this bank */
661 break;
662 }
663
664 /* FIXME This needlessly touches sectors BETWEEN the
665 * sections it's writing. Without auto erase, it just
666 * writes ones. That WILL INVALIDATE data in cases
667 * like Stellaris Tempest chips, corrupting internal
668 * ECC codes; and at least FreeScale suggests issues
669 * with that approach (in HC11 documentation).
670 *
671 * With auto erase enabled, data in those sectors will
672 * be needlessly destroyed; and some of the limited
673 * number of flash erase cycles will be wasted...
674 *
675 * In both cases, the extra writes slow things down.
676 */
677
678 /* if we have multiple sections within our image,
679 * flash programming could fail due to alignment issues
680 * attempt to rebuild a consecutive buffer for the flash loader */
681 pad_bytes = (sections[section_last + 1]->base_address) - (run_address + run_size);
682 padding[section_last] = pad_bytes;
683 run_size += sections[++section_last]->size;
684 run_size += pad_bytes;
685
686 if (pad_bytes > 0)
687 LOG_INFO("Padding image section %d with %d bytes", section_last-1, pad_bytes);
688 }
689
690 if (run_address + run_size - 1 > c->base + c->size - 1)
691 {
692 /* If we have more than one flash chip back to back, then we limit
693 * the current write operation to the current chip.
694 */
695 LOG_DEBUG("Truncate flash run size to the current flash chip.");
696
697 run_size = c->base + c->size - run_address;
698 assert(run_size > 0);
699 }
700
701 /* If we're applying any sector automagic, then pad this
702 * (maybe-combined) segment to the end of its last sector.
703 */
704 if (unlock || erase) {
705 int sector;
706 uint32_t offset_start = run_address - c->base;
707 uint32_t offset_end = offset_start + run_size;
708 uint32_t end = offset_end, delta;
709
710 for (sector = 0; sector < c->num_sectors; sector++) {
711 end = c->sectors[sector].offset
712 + c->sectors[sector].size;
713 if (offset_end <= end)
714 break;
715 }
716
717 delta = end - offset_end;
718 padding[section_last] += delta;
719 run_size += delta;
720 }
721
722 /* allocate buffer */
723 buffer = malloc(run_size);
724 if (buffer == NULL)
725 {
726 LOG_ERROR("Out of memory for flash bank buffer");
727 retval = ERROR_FAIL;
728 goto done;
729 }
730 buffer_size = 0;
731
732 /* read sections to the buffer */
733 while (buffer_size < run_size)
734 {
735 size_t size_read;
736
737 size_read = run_size - buffer_size;
738 if (size_read > sections[section]->size - section_offset)
739 size_read = sections[section]->size - section_offset;
740
741 /* KLUDGE!
742 *
743 * #¤%#"%¤% we have to figure out the section # from the sorted
744 * list of pointers to sections to invoke image_read_section()...
745 */
746 intptr_t diff = (intptr_t)sections[section] - (intptr_t)image->sections;
747 int t_section_num = diff / sizeof(struct imagesection);
748
749 LOG_DEBUG("image_read_section: section = %d, t_section_num = %d, section_offset = %d, buffer_size = %d, size_read = %d",
750 (int)section,
751 (int)t_section_num, (int)section_offset, (int)buffer_size, (int)size_read);
752 if ((retval = image_read_section(image, t_section_num, section_offset,
753 size_read, buffer + buffer_size, &size_read)) != ERROR_OK || size_read == 0)
754 {
755 free(buffer);
756 goto done;
757 }
758
759 /* see if we need to pad the section */
760 while (padding[section]--)
761 (buffer + buffer_size)[size_read++] = 0xff;
762
763 buffer_size += size_read;
764 section_offset += size_read;
765
766 if (section_offset >= sections[section]->size)
767 {
768 section++;
769 section_offset = 0;
770 }
771 }
772
773 retval = ERROR_OK;
774
775 if (unlock)
776 {
777 retval = flash_unlock_address_range(target, run_address, run_size);
778 }
779 if (retval == ERROR_OK)
780 {
781 if (erase)
782 {
783 /* calculate and erase sectors */
784 retval = flash_erase_address_range(target,
785 true, run_address, run_size);
786 }
787 }
788
789 if (retval == ERROR_OK)
790 {
791 /* write flash sectors */
792 retval = flash_driver_write(c, buffer, run_address - c->base, run_size);
793 }
794
795 free(buffer);
796
797 if (retval != ERROR_OK)
798 {
799 /* abort operation */
800 goto done;
801 }
802
803 if (written != NULL)
804 *written += run_size; /* add run size to total written counter */
805 }
806
807
808 done:
809 free(sections);
810 free(padding);
811
812 return retval;
813 }
814
815 int flash_write(struct target *target, struct image *image,
816 uint32_t *written, int erase)
817 {
818 return flash_write_unlock(target, image, written, erase, false);
819 }

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)