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

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)