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

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)