]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/flash.c
Initial revision
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / flash.c
1 //==========================================================================
2 //
3 //      flash.c
4 //
5 //      RedBoot - FLASH memory support
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
12 // Copyright (C) 2003, 2004 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas, tkoeller
46 // Date:         2000-07-28
47 // Purpose:      
48 // Description:  
49 //              
50 // This code is part of RedBoot (tm).
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 #include <redboot.h>
57 #include <cyg/io/flash.h>
58 #include <fis.h>
59 #include <sib.h>
60 #include <cyg/infra/cyg_ass.h>         // assertion macros
61
62 #ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
63 // Note horrid intertwining of functions, to save precious FLASH
64 extern void conf_endian_fixup(void *p);
65 #endif
66
67 // Round a quantity up
68 #define _rup(n,s) ((((n)+(s-1))/s)*s)
69
70 #ifdef CYGOPT_REDBOOT_FIS
71 // Image management functions
72 local_cmd_entry("init",
73                 "Initialize FLASH Image System [FIS]",
74                 "[-f]",
75                 fis_init,
76                 FIS_cmds
77     );
78 #ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
79 # define FIS_LIST_OPTS "[-c] [-d]"
80 #else
81 # define FIS_LIST_OPTS "[-d]"
82 #endif
83 local_cmd_entry("list",
84                 "Display contents of FLASH Image System [FIS]",
85                 FIS_LIST_OPTS,
86                 fis_list,
87                 FIS_cmds
88     );
89 local_cmd_entry("free",
90                 "Display free [available] locations within FLASH Image System [FIS]",
91                 "",
92                 fis_free,
93                 FIS_cmds
94     );
95 local_cmd_entry("delete",
96                 "Delete an image from FLASH Image System [FIS]",
97                 "name",
98                 fis_delete,
99                 FIS_cmds
100     );
101
102 static char fis_load_usage[] = 
103 #ifdef CYGPRI_REDBOOT_ZLIB_FLASH
104                       "[-d] "
105 #endif
106                       "[-b <memory_load_address>] [-c] name";
107
108 local_cmd_entry("load",
109                 "Load image from FLASH Image System [FIS] into RAM",
110                 fis_load_usage,
111                 fis_load,
112                 FIS_cmds
113     );
114 local_cmd_entry("create",
115                 "Create an image",
116                 "[-b <mem_base>] [-l <image_length>] [-s <data_length>]\n"
117                 "      [-f <flash_addr>] [-e <entry_point>] [-r <ram_addr>] [-n] <name>",
118                 fis_create,
119                 FIS_cmds
120     );
121 #endif
122
123 // Raw flash access functions
124 local_cmd_entry("erase",
125                 "Erase FLASH contents",
126                 "-f <flash_addr> -l <length>",
127                 fis_erase,
128                 FIS_cmds
129     );
130 #ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING
131 local_cmd_entry("lock",
132                 "LOCK FLASH contents",
133                 "[-f <flash_addr> -l <length>] [name]",
134                 fis_lock,
135                 FIS_cmds
136     );
137 local_cmd_entry("unlock",
138                 "UNLOCK FLASH contents",
139                 "[-f <flash_addr> -l <length>] [name]",
140                 fis_unlock,
141                 FIS_cmds
142     );
143 #endif
144 local_cmd_entry("write",
145                 "Write raw data directly to FLASH",
146                 "-f <flash_addr> -b <mem_base> -l <image_length>",
147                 fis_write,
148                 FIS_cmds
149     );
150
151 // Define table boundaries
152 CYG_HAL_TABLE_BEGIN( __FIS_cmds_TAB__, FIS_cmds);
153 CYG_HAL_TABLE_END( __FIS_cmds_TAB_END__, FIS_cmds);
154
155 extern struct cmd __FIS_cmds_TAB__[], __FIS_cmds_TAB_END__;
156
157 // CLI function
158 static cmd_fun do_fis;
159 RedBoot_nested_cmd("fis", 
160             "Manage FLASH images", 
161             "{cmds}",
162             do_fis,
163             __FIS_cmds_TAB__, &__FIS_cmds_TAB_END__
164     );
165
166 // Local data used by these routines
167 void *flash_start, *flash_end;
168 int flash_block_size, flash_num_blocks;
169 #ifdef CYGOPT_REDBOOT_FIS
170 void *fis_work_block;
171 void *fis_addr;
172 int fisdir_size;  // Size of FIS directory.
173 #endif
174 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG
175 extern void *cfg_base;   // Location in Flash of config data
176 extern int   cfg_size;   // Length of config data - rounded to Flash block size
177 extern struct _config *config;
178 #endif
179
180 static void
181 fis_usage(char *why)
182 {
183     diag_printf("*** invalid 'fis' command: %s\n", why);
184     cmd_usage(__FIS_cmds_TAB__, &__FIS_cmds_TAB_END__, "fis ");
185 }
186
187 static void        
188 _show_invalid_flash_address(CYG_ADDRESS flash_addr, int stat)
189 {
190     diag_printf("Invalid FLASH address %p: %s\n", (void *)flash_addr, flash_errmsg(stat));
191     diag_printf("   valid range is %p-%p\n", (void *)flash_start, (void *)flash_end);
192 }
193
194 #ifdef CYGOPT_REDBOOT_FIS
195
196 // fis_endian_fixup() is used to swap endianess if required.
197 //
198 static inline void fis_endian_fixup(void *addr)
199 {
200 #ifdef REDBOOT_FLASH_REVERSE_BYTEORDER
201     struct fis_image_desc *p = addr;
202     int cnt = fisdir_size / sizeof(struct fis_image_desc);
203
204     while (cnt-- > 0) {
205         p->flash_base = CYG_SWAP32(p->flash_base);
206         p->mem_base = CYG_SWAP32(p->mem_base);
207         p->size = CYG_SWAP32(p->size);
208         p->entry_point = CYG_SWAP32(p->entry_point);
209         p->data_length = CYG_SWAP32(p->data_length);
210         p->desc_cksum = CYG_SWAP32(p->desc_cksum);
211         p->file_cksum = CYG_SWAP32(p->file_cksum);
212         p++;
213     }
214 #endif
215 }
216
217 void
218 fis_read_directory(void)
219 {
220     void *err_addr;
221
222     FLASH_READ(fis_addr, fis_work_block, fisdir_size, (void **)&err_addr);
223     fis_endian_fixup(fis_work_block);
224 }
225
226 struct fis_image_desc *
227 fis_lookup(char *name, int *num)
228 {
229     int i;
230     struct fis_image_desc *img;
231
232     fis_read_directory();
233
234     img = (struct fis_image_desc *)fis_work_block;
235     for (i = 0;  i < fisdir_size/sizeof(*img);  i++, img++) {
236         if ((img->name[0] != (unsigned char)0xFF) && 
237             (strcasecmp(name, img->name) == 0)) {
238             if (num) *num = i;
239             return img;
240         }
241     }
242     return (struct fis_image_desc *)0;
243 }
244
245 void
246 fis_update_directory(void)
247 {
248     int stat;
249     void *err_addr;
250
251     fis_endian_fixup(fis_work_block);
252 #ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
253     memcpy((char *)fis_work_block+fisdir_size, config, cfg_size);
254     conf_endian_fixup((char *)fis_work_block+fisdir_size);
255 #endif
256 #ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
257     // Ensure [quietly] that the directory is unlocked before trying to update
258     flash_unlock((void *)fis_addr, flash_block_size, (void **)&err_addr);
259 #endif
260     if ((stat = flash_erase(fis_addr, flash_block_size, (void **)&err_addr)) != 0) {
261         diag_printf("Error erasing FIS directory at %p: %s\n", err_addr, flash_errmsg(stat));
262     } else {
263         if ((stat = FLASH_PROGRAM(fis_addr, fis_work_block,
264                                   flash_block_size, (void **)&err_addr)) != 0) {
265             diag_printf("Error writing FIS directory at %p: %s\n", 
266                         err_addr, flash_errmsg(stat));
267         }
268     }
269 #ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
270     // Ensure [quietly] that the directory is locked after the update
271     flash_lock((void *)fis_addr, flash_block_size, (void **)&err_addr);
272 #endif
273     fis_endian_fixup(fis_work_block);
274 }
275
276 static void
277 fis_init(int argc, char *argv[])
278 {
279     int stat;
280     struct fis_image_desc *img;
281     void *err_addr;
282     bool full_init = false;
283     struct option_info opts[1];
284     CYG_ADDRESS redboot_flash_start;
285     unsigned long redboot_image_size;
286
287     init_opts(&opts[0], 'f', false, OPTION_ARG_TYPE_FLG, 
288               (void *)&full_init, (bool *)0, "full initialization, erases all of flash");
289     if (!scan_opts(argc, argv, 2, opts, 1, 0, 0, ""))
290     {
291         return;
292     }
293
294     if (!verify_action("About to initialize [format] FLASH image system")) {
295         diag_printf("** Aborted\n");
296         return;
297     }
298     diag_printf("*** Initialize FLASH Image System\n");
299
300 #define MIN_REDBOOT_IMAGE_SIZE CYGBLD_REDBOOT_MIN_IMAGE_SIZE
301     redboot_image_size = flash_block_size > MIN_REDBOOT_IMAGE_SIZE ? 
302                          flash_block_size : MIN_REDBOOT_IMAGE_SIZE;
303
304     // Create a pseudo image for RedBoot
305     img = (struct fis_image_desc *)fis_work_block;
306     memset(img, 0xFF, fisdir_size);  // Start with erased data
307 #ifdef CYGOPT_REDBOOT_FIS_RESERVED_BASE
308     memset(img, 0, sizeof(*img));
309     strcpy(img->name, "(reserved)");
310     img->flash_base = (CYG_ADDRESS)flash_start;
311     img->mem_base = (CYG_ADDRESS)flash_start;
312     img->size = CYGNUM_REDBOOT_FLASH_RESERVED_BASE;
313     img++;
314 #endif
315     redboot_flash_start = (CYG_ADDRESS)flash_start + CYGBLD_REDBOOT_FLASH_BOOT_OFFSET;
316 #ifdef CYGOPT_REDBOOT_FIS_REDBOOT
317     memset(img, 0, sizeof(*img));
318     strcpy(img->name, "RedBoot");
319     img->flash_base = redboot_flash_start;
320     img->mem_base = redboot_flash_start;
321     img->size = redboot_image_size;
322     img++;
323     redboot_flash_start += redboot_image_size;
324 #endif
325 #ifdef CYGOPT_REDBOOT_FIS_REDBOOT_POST
326 #ifdef CYGNUM_REDBOOT_FIS_REDBOOT_POST_OFFSET
327     // Take care to place the POST entry at the right offset:
328     redboot_flash_start = (CYG_ADDRESS)flash_start + CYGNUM_REDBOOT_FIS_REDBOOT_POST_OFFSET;
329 #endif
330     memset(img, 0, sizeof(*img));
331     strcpy(img->name, "RedBoot[post]");
332     img->flash_base = redboot_flash_start;
333     img->mem_base = redboot_flash_start;
334     img->size = redboot_image_size;
335     img++;
336     redboot_flash_start += redboot_image_size;
337 #endif
338 #ifdef CYGOPT_REDBOOT_FIS_REDBOOT_BACKUP
339     // And a backup image
340     memset(img, 0, sizeof(*img));
341     strcpy(img->name, "RedBoot[backup]");
342     img->flash_base = redboot_flash_start;
343     img->mem_base = redboot_flash_start;
344     img->size = redboot_image_size;
345     img++;
346     redboot_flash_start += redboot_image_size;
347 #endif
348 #if defined(CYGSEM_REDBOOT_FLASH_CONFIG) && defined(CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH)
349     // And a descriptor for the configuration data
350     memset(img, 0, sizeof(*img));
351     strcpy(img->name, "RedBoot config");
352     img->flash_base = (CYG_ADDRESS)cfg_base;
353     img->mem_base = (CYG_ADDRESS)cfg_base;
354     img->size = cfg_size;
355     img++;
356 #endif
357     // And a descriptor for the descriptor table itself
358     memset(img, 0, sizeof(*img));
359     strcpy(img->name, "FIS directory");
360     img->flash_base = (CYG_ADDRESS)fis_addr;
361     img->mem_base = (CYG_ADDRESS)fis_addr;
362     img->size = fisdir_size;
363     img++;
364
365 #ifdef CYGOPT_REDBOOT_FIS_DIRECTORY_ARM_SIB_ID
366     // FIS gets the size of a full block - note, this should be changed
367     // if support is added for multi-block FIS structures.
368     img = (struct fis_image_desc *)((CYG_ADDRESS)fis_work_block + fisdir_size);
369     // Add a footer so the FIS will be recognized by the ARM Boot
370     // Monitor as a reserved area.
371     {
372         tFooter* footer_p = (tFooter*)((CYG_ADDRESS)img - sizeof(tFooter));
373         cyg_uint32 check = 0;
374         cyg_uint32 *check_ptr = (cyg_uint32 *)footer_p;
375         cyg_int32 count = (sizeof(tFooter) - 4) >> 2;
376
377         // Prepare footer. Try to protect all but the reserved space
378         // and the first RedBoot image (which is expected to be
379         // bootable), but fall back to just protecting the FIS if it's
380         // not at the default position in the flash.
381 #if defined(CYGOPT_REDBOOT_FIS_RESERVED_BASE) && (-1 == CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK)
382         footer_p->blockBase = (char*)_ADDR_REDBOOT_TO_ARM(flash_start);
383         footer_p->blockBase += CYGNUM_REDBOOT_FLASH_RESERVED_BASE + redboot_image_size;
384 #else
385         footer_p->blockBase = (char*)_ADDR_REDBOOT_TO_ARM(fis_work_block);
386 #endif
387         footer_p->infoBase = NULL;
388         footer_p->signature = FLASH_FOOTER_SIGNATURE;
389         footer_p->type = TYPE_REDHAT_REDBOOT;
390
391         // and compute its checksum
392         for ( ; count > 0; count--) {
393             if (*check_ptr > ~check)
394                 check++;
395             check += *check_ptr++;
396         }
397         footer_p->checksum = ~check;
398     }
399 #endif
400
401     // Do this after creating the initialized table because that inherently
402     // calculates where the high water mark of default RedBoot images is.
403
404     if (full_init) {
405         unsigned long erase_size;
406         CYG_ADDRESS erase_start;
407         // Erase everything except default RedBoot images, fis block, 
408         // and config block.
409         // First deal with the possible first part, before RedBoot images:
410 #if (CYGBLD_REDBOOT_FLASH_BOOT_OFFSET > CYGNUM_REDBOOT_FLASH_RESERVED_BASE)
411         erase_start = (CYG_ADDRESS)flash_start + CYGNUM_REDBOOT_FLASH_RESERVED_BASE;
412         erase_size =  (CYG_ADDRESS)flash_start + CYGBLD_REDBOOT_FLASH_BOOT_OFFSET;
413         if ( erase_size > erase_start ) {
414             erase_size -= erase_start;
415             if ((stat = flash_erase((void *)erase_start, erase_size,
416                                     (void **)&err_addr)) != 0) {
417                 diag_printf("   initialization failed at %p: %s\n",
418                             err_addr, flash_errmsg(stat));
419             }
420         }
421 #endif
422         // second deal with the larger part in the main:
423         erase_start = redboot_flash_start; // high water of created images
424         // Now the empty bits between the end of Redboot and the cfg and dir 
425         // blocks. 
426 #if defined(CYGSEM_REDBOOT_FLASH_CONFIG) && \
427     defined(CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH) && \
428     !defined(CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG)
429         if (fis_addr > cfg_base) {
430           erase_size = (CYG_ADDRESS)cfg_base - erase_start; // the gap between HWM and config data
431         } else {
432           erase_size = (CYG_ADDRESS)fis_addr - erase_start; // the gap between HWM and fis data
433         }
434         if ((stat = flash_erase((void *)erase_start, erase_size,
435                                 (void **)&err_addr)) != 0) {
436           diag_printf("   initialization failed %p: %s\n",
437                  err_addr, flash_errmsg(stat));
438         }
439         erase_start += (erase_size + flash_block_size);
440         if (fis_addr > cfg_base) {
441           erase_size = (CYG_ADDRESS)fis_addr - erase_start; // the gap between config and fis data
442         } else {
443           erase_size = (CYG_ADDRESS)cfg_base - erase_start; // the gap between fis and config data
444         }
445         if ((stat = flash_erase((void *)erase_start, erase_size,
446                                 (void **)&err_addr)) != 0) {
447           diag_printf("   initialization failed %p: %s\n",
448                  err_addr, flash_errmsg(stat));
449         }
450         erase_start += (erase_size + flash_block_size);
451 #else  // !CYGSEM_REDBOOT_FLASH_CONFIG        
452         erase_size = (CYG_ADDRESS)fis_addr - erase_start; // the gap between HWM and fis data
453         if ((stat = flash_erase((void *)erase_start, erase_size,
454                                 (void **)&err_addr)) != 0) {
455           diag_printf("   initialization failed %p: %s\n",
456                  err_addr, flash_errmsg(stat));
457         }
458         erase_start += (erase_size + flash_block_size);          
459 #endif
460         // Lastly, anything at the end, if there is any
461         if ( erase_start < (((CYG_ADDRESS)flash_end)+1) ) {
462             erase_size = ((CYG_ADDRESS)flash_end - erase_start) + 1;
463             if ((stat = flash_erase((void *)erase_start, erase_size,
464                                     (void **)&err_addr)) != 0) {
465                 diag_printf("   initialization failed at %p: %s\n",
466                             err_addr, flash_errmsg(stat));
467             }
468         }
469 #ifndef CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS
470     // In this case, 'fis free' works by scanning for erased blocks.  Since the
471     // "-f" option was not supplied, there may be areas which are not used but
472     // don't appear to be free since they are not erased - thus the warning
473     } else {
474         diag_printf("    Warning: device contents not erased, some blocks may not be usable\n");
475 #endif
476     }
477     fis_update_directory();
478 }
479
480 static void
481 fis_list(int argc, char *argv[])
482 {
483     struct fis_image_desc *img;
484     int i, image_indx;
485     bool show_cksums = false;
486     bool show_datalen = false;
487     struct option_info opts[2];
488     unsigned long last_addr, lowest_addr;
489     bool image_found;
490
491 #ifdef CYGHWR_REDBOOT_ARM_FLASH_SIB
492     // FIXME: this is somewhat half-baked
493     extern void arm_fis_list(void);
494     arm_fis_list();
495     return;
496 #endif
497
498     init_opts(&opts[0], 'd', false, OPTION_ARG_TYPE_FLG, 
499               (void *)&show_datalen, (bool *)0, "display data length");
500 #ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
501     init_opts(&opts[1], 'c', false, OPTION_ARG_TYPE_FLG, 
502               (void *)&show_cksums, (bool *)0, "display checksums");
503     i = 2;
504 #else
505     i = 1;
506 #endif
507     if (!scan_opts(argc, argv, 2, opts, i, 0, 0, "")) {
508         return;
509     }
510     fis_read_directory();
511
512     // Let diag_printf do the formatting in both cases, rather than counting
513     // cols by hand....
514     diag_printf("%-16s  %-10s  %-10s  %-10s  %-s\n",
515                 "Name","FLASH addr",
516                 show_cksums ? "Checksum" : "Mem addr",
517                 show_datalen ? "Datalen" : "Length",
518                 "Entry point" );
519     last_addr = 0;
520     image_indx = 0;
521     do {
522         image_found = false;
523         lowest_addr = 0xFFFFFFFF;
524         img = (struct fis_image_desc *) fis_work_block;
525         for (i = 0;  i < fisdir_size/sizeof(*img);  i++, img++) {
526             if (img->name[0] != (unsigned char)0xFF) {
527                 if ((img->flash_base > last_addr) && (img->flash_base < lowest_addr)) {
528                     lowest_addr = img->flash_base;
529                     image_found = true;
530                     image_indx = i;
531                 }
532             }
533         }
534         if (image_found) {
535             img = (struct fis_image_desc *) fis_work_block;
536             img += image_indx;
537             diag_printf("%-16s  0x%08lX  0x%08lX  0x%08lX  0x%08lX\n", img->name, 
538                         (unsigned long)img->flash_base, 
539 #ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
540                         show_cksums ? img->file_cksum : img->mem_base, 
541                         show_datalen ? img->data_length : img->size, 
542 #else
543                         img->mem_base, 
544                         img->size, 
545 #endif
546                         (unsigned long)img->entry_point);
547         }
548         last_addr = lowest_addr;
549     } while (image_found == true);
550 }
551
552 #ifdef CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS
553 struct free_chunk {
554     CYG_ADDRESS start, end;
555 };
556
557 static int
558 find_free(struct free_chunk *chunks)
559 {
560     CYG_ADDRESS *fis_ptr, *fis_end;
561     struct fis_image_desc *img;
562     int i, idx;
563     int num_chunks = 1;
564
565     // Do not search the area reserved for pre-RedBoot systems:
566     fis_ptr = (CYG_ADDRESS *)((CYG_ADDRESS)flash_start + 
567                               CYGNUM_REDBOOT_FLASH_RESERVED_BASE);
568     fis_end = (CYG_ADDRESS *)flash_end;
569     chunks[num_chunks-1].start = (CYG_ADDRESS)fis_ptr;
570     chunks[num_chunks-1].end = (CYG_ADDRESS)fis_end;
571     fis_read_directory();
572     img = (struct fis_image_desc *) fis_work_block;
573     for (i = 0;  i < fisdir_size/sizeof(*img);  i++, img++) {
574         if (img->name[0] != (unsigned char)0xFF) {
575             // Figure out which chunk this is in and split it
576             for (idx = 0;  idx < num_chunks;  idx++) {
577                 if ((img->flash_base >= chunks[idx].start) && 
578                     (img->flash_base <= chunks[idx].end)) {
579                     if (img->flash_base == chunks[idx].start) {
580                         chunks[idx].start += img->size;
581                         if (chunks[idx].start >= chunks[idx].end) {
582                             // This free chunk has collapsed
583                             while (idx < (num_chunks-1)) {
584                                 chunks[idx] = chunks[idx+1];
585                                 idx++;
586                             }
587                             num_chunks--;
588                         }
589                     } else if ((img->flash_base+img->size) == chunks[idx].end) {
590                         chunks[idx].end = img->flash_base;
591                     } else {
592                         // Split chunk into two parts
593                         if ((img->flash_base+img->size) < (CYG_ADDRESS)fis_end) {
594                             int j;
595                             // make room for new chunk
596                             for (j = num_chunks; j > (idx+1); j--)
597                                 chunks[j] = chunks[j-1];
598                             chunks[idx+1].start = img->flash_base + img->size;
599                             chunks[idx+1].end = chunks[idx].end;
600                             if (++num_chunks == CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS) {
601                                 diag_printf("Warning: too many free chunks\n");
602                                 return num_chunks;
603                             }
604                         }
605                         chunks[idx].end = img->flash_base;
606                     }
607                     break;
608                 }
609             }
610         }
611     }
612     return num_chunks;
613 }
614 #endif // CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS
615
616 static void
617 fis_free(int argc, char *argv[])
618 {
619 #ifndef CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS
620     unsigned long *fis_ptr, *fis_end, flash_data;
621     unsigned long *area_start;
622     void *err_addr;
623
624     // Do not search the area reserved for pre-RedBoot systems:
625     fis_ptr = (unsigned long *)((CYG_ADDRESS)flash_start + 
626                                 CYGNUM_REDBOOT_FLASH_RESERVED_BASE);
627     fis_end = (unsigned long *)(CYG_ADDRESS)flash_end;
628     area_start = fis_ptr;
629     while (fis_ptr < fis_end) {
630         flash_read(fis_ptr, &flash_data, sizeof(unsigned long), (void **)&err_addr);
631         if (flash_data != (unsigned long)0xFFFFFFFF) {
632             if (area_start != fis_ptr) {
633                 // Assume that this is something
634                 diag_printf("  0x%08lX .. 0x%08lX\n",
635                             (CYG_ADDRESS)area_start, (CYG_ADDRESS)fis_ptr);
636             }
637             // Find next blank block
638             area_start = fis_ptr;
639             while (area_start < fis_end) {
640                 flash_read(area_start, &flash_data, sizeof(unsigned long), (void **)&err_addr);
641                 if (flash_data == (unsigned long)0xFFFFFFFF) {
642                     break;
643                 }
644                 area_start += flash_block_size / sizeof(CYG_ADDRESS);
645             }
646             fis_ptr = area_start;
647         } else {
648             fis_ptr += flash_block_size / sizeof(CYG_ADDRESS);
649         }
650     }
651     if (area_start != fis_ptr) {
652         diag_printf("  0x%08lX .. 0x%08lX\n", 
653                     (CYG_ADDRESS)area_start, (CYG_ADDRESS)fis_ptr);
654     }
655 #else
656     struct free_chunk chunks[CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS];
657     int idx, num_chunks;
658
659     num_chunks = find_free(chunks);
660     for (idx = 0;  idx < num_chunks;  idx++) {
661         diag_printf("  0x%08lX .. 0x%08lX\n", 
662                     (unsigned long)chunks[idx].start,
663                     (unsigned long)chunks[idx].end);
664     }
665 #endif
666 }
667
668 // Find the first unused area of flash which is long enough
669 static bool
670 fis_find_free(CYG_ADDRESS *addr, unsigned long length)
671 {
672 #ifndef CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS
673     unsigned long *fis_ptr, *fis_end, flash_data;
674     unsigned long *area_start;
675     void *err_addr;
676
677     // Do not search the area reserved for pre-RedBoot systems:
678     fis_ptr = (unsigned long *)((CYG_ADDRESS)flash_start + 
679                                 CYGNUM_REDBOOT_FLASH_RESERVED_BASE);
680     fis_end = (unsigned long *)(CYG_ADDRESS)flash_end;
681     area_start = fis_ptr;
682     while (fis_ptr < fis_end) {
683         flash_read(fis_ptr, &flash_data, sizeof(unsigned long), (void **)&err_addr);
684         if (flash_data != (unsigned long)0xFFFFFFFF) {
685             if (area_start != fis_ptr) {
686                 // Assume that this is something
687                 if ((fis_ptr-area_start) >= (length/sizeof(unsigned))) {
688                     *addr = (CYG_ADDRESS)area_start;
689                     return true;
690                 }
691             }
692             // Find next blank block
693             area_start = fis_ptr;
694             while (area_start < fis_end) {
695                 flash_read(area_start, &flash_data, sizeof(unsigned long), (void **)&err_addr);
696                 if (flash_data == (unsigned long)0xFFFFFFFF) {
697                     break;
698                 }
699                 area_start += flash_block_size / sizeof(CYG_ADDRESS);
700             }
701             fis_ptr = area_start;
702         } else {
703             fis_ptr += flash_block_size / sizeof(CYG_ADDRESS);
704         }
705     }
706     if (area_start != fis_ptr) {
707         if ((fis_ptr-area_start) >= (length/sizeof(unsigned))) {
708             *addr = (CYG_ADDRESS)area_start;
709             return true;
710         }
711     }
712     return false;
713 #else
714     struct free_chunk chunks[CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS];
715     int idx, num_chunks;
716
717     num_chunks = find_free(chunks);
718     for (idx = 0;  idx < num_chunks;  idx++) {
719         if ((chunks[idx].end - chunks[idx].start) >= length) {
720             *addr = (CYG_ADDRESS)chunks[idx].start;
721             return true;
722         }
723     }
724     return false;
725 #endif
726 }
727
728 static void
729 fis_create(int argc, char *argv[])
730 {
731     int i, stat;
732     unsigned long length, img_size;
733     CYG_ADDRESS mem_addr, exec_addr, flash_addr, entry_addr;
734     char *name;
735     bool mem_addr_set = false;
736     bool exec_addr_set = false;
737     bool entry_addr_set = false;
738     bool flash_addr_set = false;
739     bool length_set = false;
740     bool img_size_set = false;
741     bool no_copy = false;
742     void *err_addr;
743     struct fis_image_desc *img = NULL;
744     bool defaults_assumed;
745     struct option_info opts[7];
746     bool prog_ok = true;
747
748     init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM, 
749               (void *)&mem_addr, (bool *)&mem_addr_set, "memory base address");
750     init_opts(&opts[1], 'r', true, OPTION_ARG_TYPE_NUM, 
751               (void *)&exec_addr, (bool *)&exec_addr_set, "ram base address");
752     init_opts(&opts[2], 'e', true, OPTION_ARG_TYPE_NUM, 
753               (void *)&entry_addr, (bool *)&entry_addr_set, "entry point address");
754     init_opts(&opts[3], 'f', true, OPTION_ARG_TYPE_NUM, 
755               (void *)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
756     init_opts(&opts[4], 'l', true, OPTION_ARG_TYPE_NUM, 
757               (void *)&length, (bool *)&length_set, "image length [in FLASH]");
758     init_opts(&opts[5], 's', true, OPTION_ARG_TYPE_NUM, 
759               (void *)&img_size, (bool *)&img_size_set, "image size [actual data]");
760     init_opts(&opts[6], 'n', false, OPTION_ARG_TYPE_FLG, 
761               (void *)&no_copy, (bool *)0, "don't copy from RAM to FLASH, just update directory");
762     if (!scan_opts(argc, argv, 2, opts, 7, (void *)&name, OPTION_ARG_TYPE_STR, "file name"))
763     {
764         fis_usage("invalid arguments");
765         return;
766     }
767
768     fis_read_directory();
769     defaults_assumed = false;
770     if (name) {
771         // Search existing files to acquire defaults for params not specified:
772         img = fis_lookup(name, NULL);
773         if (img) {
774             // Found it, so get image size from there
775             if (!length_set) {
776                 length_set = true;
777                 length = img->size;
778                 defaults_assumed = true;
779             }
780         }
781     }
782     if (!mem_addr_set && (load_address >= (CYG_ADDRESS)ram_start) &&
783         (load_address_end) < (CYG_ADDRESS)ram_end) {
784         mem_addr = load_address;
785         mem_addr_set = true;
786         defaults_assumed = true;
787         // Get entry address from loader, unless overridden
788         if (!entry_addr_set)
789             entry_addr = entry_address;
790         if (!length_set) {
791             length = load_address_end - load_address;
792             length_set = true;
793         } else if (defaults_assumed && !img_size_set) {
794             /* We got length from the FIS table, so the size of the
795                actual loaded image becomes img_size */
796             img_size = load_address_end - load_address;
797             img_size_set = true;
798         }
799     }
800     // Get the remaining fall-back values from the fis
801     if (img) {
802         if (!exec_addr_set) {
803             // Preserve "normal" behaviour
804             exec_addr_set = true;
805             exec_addr = flash_addr_set ? flash_addr : mem_addr;
806         }
807         if (!flash_addr_set) {
808             flash_addr_set = true;
809             flash_addr = img->flash_base;
810             defaults_assumed = true;
811         }
812     }
813
814     if ((!no_copy && !mem_addr_set) || (no_copy && !flash_addr_set) ||
815         !length_set || !name) {
816         fis_usage("required parameter missing");
817         return;
818     }
819     if (!img_size_set) {
820         img_size = length;
821     }
822     // 'length' is size of FLASH image, 'img_size' is actual data size
823     // Round up length to FLASH block size
824 #ifndef CYGPKG_HAL_MIPS // FIXME: compiler is b0rken
825     length = ((length + flash_block_size - 1) / flash_block_size) * flash_block_size;
826     if (length < img_size) {
827         diag_printf("Invalid FLASH image size/length combination\n");
828         return;
829     }
830 #endif
831     if (flash_addr_set &&
832         ((stat = flash_verify_addr((void *)flash_addr)) ||
833          (stat = flash_verify_addr((void *)(flash_addr+length-1))))) {
834         _show_invalid_flash_address(flash_addr, stat);
835         return;
836     }
837     if (flash_addr_set && ((flash_addr & (flash_block_size-1)) != 0)) {
838         diag_printf("Invalid FLASH address: %p\n", (void *)flash_addr);
839         diag_printf("   must be 0x%x aligned\n", flash_block_size);
840         return;
841     }
842     if (strlen(name) >= sizeof(img->name)) {
843         diag_printf("Name is too long, must be less than %d chars\n", (int)sizeof(img->name));
844         return;
845     }
846     if (!no_copy) {
847         if ((mem_addr < (CYG_ADDRESS)ram_start) ||
848             ((mem_addr+img_size) >= (CYG_ADDRESS)ram_end)) {
849             diag_printf("** WARNING: RAM address: %p may be invalid\n", (void *)mem_addr);
850             diag_printf("   valid range is %p-%p\n", (void *)ram_start, (void *)ram_end);
851         }
852         if (!flash_addr_set && !fis_find_free(&flash_addr, length)) {
853             diag_printf("Can't locate %lx(%ld) bytes free in FLASH\n", length, length);
854             return;
855         }
856     }
857     // First, see if the image by this name has agreable properties
858     if (img) {
859         if (flash_addr_set && (img->flash_base != flash_addr)) {
860             diag_printf("Image found, but flash address (%p)\n"
861                         "             is incorrect (present image location %p)\n",
862                         (void*)flash_addr, (void*)img->flash_base);
863             
864             return;
865         }
866         if (img->size != length) {
867             diag_printf("Image found, but length (0x%lx, necessitating image size 0x%lx)\n"
868                         "             is incorrect (present image size 0x%lx)\n",
869                         img_size, length, img->size);
870             return;
871         }
872         if (!verify_action("An image named '%s' exists", name)) {
873             return;
874         } else {                
875             if (defaults_assumed) {
876                 if (no_copy &&
877                     !verify_action("* CAUTION * about to program '%s'\n            at %p..%p from %p", 
878                                    name, (void *)flash_addr, (void *)(flash_addr+img_size-1),
879                                    (void *)mem_addr)) {
880                     return;  // The guy gave up
881                 }
882             }
883         }
884     } else {
885 #ifdef CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS
886         // Make sure that any FLASH address specified directly is truly free
887         if (flash_addr_set && !no_copy) {
888             struct free_chunk chunks[CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS];
889             int idx, num_chunks;
890             bool is_free = false;
891
892             num_chunks = find_free(chunks);
893             for (idx = 0;  idx < num_chunks;  idx++) {
894                 if ((flash_addr >= chunks[idx].start) && 
895                     ((flash_addr+length-1) <= chunks[idx].end)) {
896                     is_free = true;
897                 }
898             }
899             if (!is_free) {
900                 diag_printf("Invalid FLASH address - not free!\n");
901                 return;
902             }
903         }
904 #endif
905         // If not image by that name, try and find an empty slot
906         img = (struct fis_image_desc *)fis_work_block;
907         for (i = 0;  i < fisdir_size/sizeof(*img);  i++, img++) {
908             if (img->name[0] == (unsigned char)0xFF) {
909                 break;
910             }
911         }
912         if (i >= fisdir_size/sizeof(*img)) {
913             diag_printf("Can't find an empty slot in FIS directory!\n");
914             return;
915         }
916     }
917     if (!no_copy) {
918         // Safety check - make sure the address range is not within the code we're running
919         if (flash_code_overlaps((void *)flash_addr, (void *)(flash_addr+img_size-1))) {
920             diag_printf("Can't program this region - contains code in use!\n");
921             return;
922         }
923         if (prog_ok) {
924             // Erase area to be programmed
925             if ((stat = flash_erase((void *)flash_addr, length, (void **)&err_addr)) != 0) {
926                 diag_printf("Can't erase region at %p: %s\n", err_addr, flash_errmsg(stat));
927                 prog_ok = false;
928             }
929         }
930         if (prog_ok) {
931             // Now program it
932             if ((stat = FLASH_PROGRAM((void *)flash_addr, (void *)mem_addr, img_size, (void **)&err_addr)) != 0) {
933                 diag_printf("Can't program region at %p: %s\n", err_addr, flash_errmsg(stat));
934                 prog_ok = false;
935             }
936         }
937     }
938     if (prog_ok) {
939         // Update directory
940         memset(img, 0, sizeof(*img));
941         strcpy(img->name, name);
942         img->flash_base = flash_addr;
943         img->mem_base = exec_addr_set ? exec_addr : (mem_addr_set ? mem_addr : flash_addr);
944         img->entry_point = entry_addr_set ? entry_addr : (CYG_ADDRESS)entry_address;  // Hope it's been set
945         img->size = length;
946         img->data_length = img_size;
947 #ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
948         if (!no_copy) {
949             img->file_cksum = cyg_crc32((unsigned char *)mem_addr, img_size);
950         } else {
951             // No way to compute this, sorry
952             img->file_cksum = 0;
953         }
954 #endif
955         fis_update_directory();
956     }
957 }
958
959 extern void arm_fis_delete(char *);
960 static void
961 fis_delete(int argc, char *argv[])
962 {
963     char *name;
964     int num_reserved, i, stat;
965     void *err_addr;
966     struct fis_image_desc *img;
967
968     if (!scan_opts(argc, argv, 2, 0, 0, (void *)&name, OPTION_ARG_TYPE_STR, "image name"))
969     {
970         fis_usage("invalid arguments");
971         return;
972     }
973 #ifdef CYGHWR_REDBOOT_ARM_FLASH_SIB
974     // FIXME: this is somewhat half-baked
975     arm_fis_delete(name);
976     return;
977 #endif
978     img = (struct fis_image_desc *)fis_work_block;
979     num_reserved = 0;
980 #ifdef CYGOPT_REDBOOT_FIS_RESERVED_BASE
981     num_reserved++;
982 #endif
983 #ifdef CYGOPT_REDBOOT_FIS_REDBOOT
984     num_reserved++;
985 #endif
986 #ifdef CYGOPT_REDBOOT_FIS_REDBOOT_BACKUP
987     num_reserved++;
988 #endif
989 #ifdef CYGOPT_REDBOOT_FIS_REDBOOT_POST
990     num_reserved++;
991 #endif
992 #if defined(CYGSEM_REDBOOT_FLASH_CONFIG) && defined(CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH)
993     num_reserved++;
994 #endif
995 #if 1 // And the descriptor for the descriptor table itself
996     num_reserved++;
997 #endif
998
999     img = fis_lookup(name, &i);
1000     if (img) {
1001         if (i < num_reserved) {
1002             diag_printf("Sorry, '%s' is a reserved image and cannot be deleted\n", img->name);
1003             return;
1004         }
1005         if (!verify_action("Delete image '%s'", name)) {
1006             return;
1007         }
1008     } else {
1009         diag_printf("No image '%s' found\n", name);
1010         return;
1011     }
1012     // Erase Data blocks (free space)
1013     if ((stat = flash_erase((void *)img->flash_base, img->size, (void **)&err_addr)) != 0) {
1014         diag_printf("Error erasing at %p: %s\n", err_addr, flash_errmsg(stat));
1015     } else {
1016         img->name[0] = (unsigned char)0xFF;    
1017         fis_update_directory();
1018     }
1019 }
1020
1021 static void
1022 fis_load(int argc, char *argv[])
1023 {
1024     char *name;
1025     struct fis_image_desc *img;
1026     CYG_ADDRESS mem_addr;
1027     bool mem_addr_set = false;
1028     bool show_cksum = false;
1029     struct option_info opts[3];
1030 #if defined(CYGSEM_REDBOOT_FIS_CRC_CHECK)
1031     unsigned long cksum;
1032 #endif
1033     int num_options;
1034 #if defined(CYGPRI_REDBOOT_ZLIB_FLASH) ||  defined(CYGSEM_REDBOOT_FIS_CRC_CHECK)
1035     bool decompress = false;
1036 #endif
1037     void *err_addr;
1038
1039     init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM, 
1040               (void *)&mem_addr, (bool *)&mem_addr_set, "memory [load] base address");
1041     init_opts(&opts[1], 'c', false, OPTION_ARG_TYPE_FLG, 
1042               (void *)&show_cksum, (bool *)0, "display checksum");
1043     num_options = 2;
1044 #ifdef CYGPRI_REDBOOT_ZLIB_FLASH
1045     init_opts(&opts[num_options], 'd', false, OPTION_ARG_TYPE_FLG, 
1046               (void *)&decompress, 0, "decompress");
1047     num_options++;
1048 #endif
1049
1050     CYG_ASSERT(num_options <= NUM_ELEMS(opts), "Too many options");
1051
1052     if (!scan_opts(argc, argv, 2, opts, num_options, (void *)&name, OPTION_ARG_TYPE_STR, "image name"))
1053     {
1054         fis_usage("invalid arguments");
1055         return;
1056     }
1057     if ((img = fis_lookup(name, NULL)) == (struct fis_image_desc *)0) {
1058         diag_printf("No image '%s' found\n", name);
1059         return;
1060     }
1061     if (!mem_addr_set) {
1062         mem_addr = img->mem_base;
1063     }
1064     // Load image from FLASH into RAM
1065 #ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
1066     if (!valid_address((void *)mem_addr)) {
1067         diag_printf("Not a loadable image - try using -b ADDRESS option\n");
1068         return;
1069     }
1070 #endif
1071 #ifdef CYGPRI_REDBOOT_ZLIB_FLASH
1072     if (decompress) {
1073         int err;
1074         _pipe_t fis_load_pipe;
1075         _pipe_t* p = &fis_load_pipe;
1076         p->out_buf = (unsigned char*) mem_addr;
1077         p->out_max = p->out_size = -1;
1078         p->in_buf = (unsigned char*) img->flash_base;
1079         p->in_avail = img->data_length;
1080
1081         err = (*_dc_init)(p);
1082
1083         if (0 == err)
1084             err = (*_dc_inflate)(p);
1085
1086         // Free used resources, do final translation of
1087         // error value.
1088         err = (*_dc_close)(p, err);
1089
1090         if (0 != err && p->msg) {
1091             diag_printf("decompression error: %s\n", p->msg);
1092         } else {
1093             diag_printf("Image loaded from %p-%p\n", (unsigned char *)mem_addr, p->out_buf);
1094         }
1095
1096         // Set load address/top
1097         load_address = mem_addr;
1098         load_address_end = (unsigned long)p->out_buf;
1099
1100         // Reload fis directory
1101         fis_read_directory();
1102     } else // dangling block
1103 #endif
1104     {
1105         flash_read((void *)img->flash_base, (void *)mem_addr, img->data_length, (void **)&err_addr);
1106
1107         // Set load address/top
1108         load_address = mem_addr;
1109         load_address_end = mem_addr + img->data_length;
1110     }
1111     entry_address = (unsigned long)img->entry_point;
1112
1113 #ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
1114     cksum = cyg_crc32((unsigned char *)mem_addr, img->data_length);
1115     if (show_cksum) {
1116         diag_printf("Checksum: 0x%08lx\n", cksum);
1117     }
1118     // When decompressing, leave CRC checking to decompressor
1119     if (!decompress && img->file_cksum) {
1120         if (cksum != img->file_cksum) {
1121             diag_printf("** Warning - checksum failure.  stored: 0x%08lx, computed: 0x%08lx\n",
1122                         img->file_cksum, cksum);
1123             entry_address = (unsigned long)NO_MEMORY;
1124         }
1125     }
1126 #endif
1127 }
1128 #endif // CYGOPT_REDBOOT_FIS
1129
1130 static void
1131 fis_write(int argc, char *argv[])
1132 {
1133     int stat;
1134     unsigned long length;
1135     CYG_ADDRESS mem_addr, flash_addr;
1136     bool mem_addr_set = false;
1137     bool flash_addr_set = false;
1138     bool length_set = false;
1139     void *err_addr;
1140     struct option_info opts[3];
1141     bool prog_ok;
1142
1143     init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM, 
1144               (void *)&mem_addr, (bool *)&mem_addr_set, "memory base address");
1145     init_opts(&opts[1], 'f', true, OPTION_ARG_TYPE_NUM, 
1146               (void *)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
1147     init_opts(&opts[2], 'l', true, OPTION_ARG_TYPE_NUM, 
1148               (void *)&length, (bool *)&length_set, "image length [in FLASH]");
1149     if (!scan_opts(argc, argv, 2, opts, 3, 0, 0, 0))
1150     {
1151         fis_usage("invalid arguments");
1152         return;
1153     }
1154
1155     if (!mem_addr_set || !flash_addr_set || !length_set) {
1156         fis_usage("required parameter missing");
1157         return;
1158     }
1159
1160     // Round up length to FLASH block size
1161 #ifndef CYGPKG_HAL_MIPS // FIXME: compiler is b0rken
1162     length = ((length + flash_block_size - 1) / flash_block_size) * flash_block_size;
1163 #endif
1164     if (flash_addr_set &&
1165         ((stat = flash_verify_addr((void *)flash_addr)) ||
1166          (stat = flash_verify_addr((void *)(flash_addr+length-1))))) {
1167         _show_invalid_flash_address(flash_addr, stat);
1168         return;
1169     }
1170     if (flash_addr_set && flash_addr & (flash_block_size-1)) {
1171         diag_printf("Invalid FLASH address: %p\n", (void *)flash_addr);
1172         diag_printf("   must be 0x%x aligned\n", flash_block_size);
1173         return;
1174     }
1175     if ((mem_addr < (CYG_ADDRESS)ram_start) ||
1176         ((mem_addr+length) >= (CYG_ADDRESS)ram_end)) {
1177         diag_printf("** WARNING: RAM address: %p may be invalid\n", (void *)mem_addr);
1178         diag_printf("   valid range is %p-%p\n", (void *)ram_start, (void *)ram_end);
1179     }
1180     // Safety check - make sure the address range is not within the code we're running
1181     if (flash_code_overlaps((void *)flash_addr, (void *)(flash_addr+length-1))) {
1182         diag_printf("Can't program this region - contains code in use!\n");
1183         return;
1184     }
1185     if (!verify_action("* CAUTION * about to program FLASH\n            at %p..%p from %p", 
1186                        (void *)flash_addr, (void *)(flash_addr+length-1),
1187                        (void *)mem_addr)) {
1188         return;  // The guy gave up
1189     }
1190     prog_ok = true;
1191     if (prog_ok) {
1192         // Erase area to be programmed
1193         if ((stat = flash_erase((void *)flash_addr, length, (void **)&err_addr)) != 0) {
1194             diag_printf("Can't erase region at %p: %s\n", err_addr, flash_errmsg(stat));
1195             prog_ok = false;
1196         }
1197     }
1198     if (prog_ok) {
1199         // Now program it
1200         if ((stat = FLASH_PROGRAM((void *)flash_addr, (void *)mem_addr, length, (void **)&err_addr)) != 0) {
1201             diag_printf("Can't program region at %p: %s\n", err_addr, flash_errmsg(stat));
1202             prog_ok = false;
1203         }
1204     }
1205 }
1206
1207 static void
1208 fis_erase(int argc, char *argv[])
1209 {
1210     int stat;
1211     unsigned long length;
1212     CYG_ADDRESS flash_addr;
1213     bool flash_addr_set = false;
1214     bool length_set = false;
1215     void *err_addr;
1216     struct option_info opts[2];
1217
1218     init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM, 
1219               (void *)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
1220     init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM, 
1221               (void *)&length, (bool *)&length_set, "length");
1222     if (!scan_opts(argc, argv, 2, opts, 2, (void **)0, 0, ""))
1223     {
1224         fis_usage("invalid arguments");
1225         return;
1226     }
1227
1228     if (!flash_addr_set || !length_set) {
1229         fis_usage("missing argument");
1230         return;
1231     }
1232     if (flash_addr_set &&
1233         ((stat = flash_verify_addr((void *)flash_addr)) ||
1234          (stat = flash_verify_addr((void *)(flash_addr+length-1))))) {
1235         _show_invalid_flash_address(flash_addr, stat);
1236         return;
1237     }
1238     if (flash_addr_set && flash_addr & (flash_block_size-1)) {
1239         diag_printf("Invalid FLASH address: %p\n", (void *)flash_addr);
1240         diag_printf("   must be 0x%x aligned\n", flash_block_size);
1241         return;
1242     }
1243     // Safety check - make sure the address range is not within the code we're running
1244     if (flash_code_overlaps((void *)flash_addr, (void *)(flash_addr+length-1))) {
1245         diag_printf("Can't erase this region - contains code in use!\n");
1246         return;
1247     }
1248     if ((stat = flash_erase((void *)flash_addr, length, (void **)&err_addr)) != 0) {
1249         diag_printf("Error erasing at %p: %s\n", err_addr, flash_errmsg(stat));
1250     }
1251 }
1252
1253 #ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING
1254
1255 static void
1256 fis_lock(int argc, char *argv[])
1257 {
1258     char *name;
1259     int stat;
1260     unsigned long length;
1261     CYG_ADDRESS flash_addr;
1262     bool flash_addr_set = false;
1263     bool length_set = false;
1264     void *err_addr;
1265     struct option_info opts[2];
1266
1267     init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM, 
1268               (void *)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
1269     init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM, 
1270               (void *)&length, (bool *)&length_set, "length");
1271     if (!scan_opts(argc, argv, 2, opts, 2, &name, OPTION_ARG_TYPE_STR, "image name"))
1272     {
1273         fis_usage("invalid arguments");
1274         return;
1275     }
1276
1277     /* Get parameters from image if specified */
1278     if (name) {
1279         struct fis_image_desc *img;
1280         if ((img = fis_lookup(name, NULL)) == (struct fis_image_desc *)0) {
1281             diag_printf("No image '%s' found\n", name);
1282             return;
1283         }
1284
1285         flash_addr = img->flash_base;
1286         length = img->size;
1287     } else if (!flash_addr_set || !length_set) {
1288         fis_usage("missing argument");
1289         return;
1290     }
1291     if (flash_addr_set &&
1292         ((stat = flash_verify_addr((void *)flash_addr)) ||
1293          (stat = flash_verify_addr((void *)(flash_addr+length-1))))) {
1294         _show_invalid_flash_address(flash_addr, stat);
1295         return;
1296     }
1297     if ((stat = flash_lock((void *)flash_addr, length, (void **)&err_addr)) != 0) {
1298         diag_printf("Error locking at %p: %s\n", err_addr, flash_errmsg(stat));
1299     }
1300 }
1301
1302 static void
1303 fis_unlock(int argc, char *argv[])
1304 {
1305     char *name;
1306     int stat;
1307     unsigned long length;
1308     CYG_ADDRESS flash_addr;
1309     bool flash_addr_set = false;
1310     bool length_set = false;
1311     void *err_addr;
1312     struct option_info opts[2];
1313
1314     init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM, 
1315               (void *)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
1316     init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM, 
1317               (void *)&length, (bool *)&length_set, "length");
1318     if (!scan_opts(argc, argv, 2, opts, 2, &name, OPTION_ARG_TYPE_STR, "image name"))
1319     {
1320         fis_usage("invalid arguments");
1321         return;
1322     }
1323
1324     if (name) {
1325         struct fis_image_desc *img;
1326         if ((img = fis_lookup(name, NULL)) == (struct fis_image_desc *)0) {
1327             diag_printf("No image '%s' found\n", name);
1328             return;
1329         }
1330
1331         flash_addr = img->flash_base;
1332         length = img->size;
1333     } else  if (!flash_addr_set || !length_set) {
1334         fis_usage("missing argument");
1335         return;
1336     }
1337     if (flash_addr_set &&
1338         ((stat = flash_verify_addr((void *)flash_addr)) ||
1339          (stat = flash_verify_addr((void *)(flash_addr+length-1))))) {
1340         _show_invalid_flash_address(flash_addr, stat);
1341         return;
1342     }
1343
1344     if ((stat = flash_unlock((void *)flash_addr, length, (void **)&err_addr)) != 0) {
1345         diag_printf("Error unlocking at %p: %s\n", err_addr, flash_errmsg(stat));
1346     }
1347 }
1348 #endif
1349
1350 // This is set non-zero if the FLASH subsystem has successfully been initialized
1351 int __flash_init = 0;
1352
1353 void
1354 _flash_info(void)
1355 {
1356     if (!__flash_init) return;
1357     diag_printf("FLASH: %p - 0x%x, %d blocks of %p bytes each.\n", 
1358            flash_start, (CYG_ADDRWORD)flash_end + 1, flash_num_blocks, (void *)flash_block_size);
1359 }
1360
1361 bool
1362 do_flash_init(void)
1363 {
1364     int stat;
1365
1366     if (!__flash_init) {
1367         __flash_init = 1;
1368         if ((stat = flash_init(diag_printf)) != 0) {
1369             diag_printf("FLASH: driver init failed: %s\n", flash_errmsg(stat));
1370             return false;
1371         }
1372         flash_get_limits((void *)0, (void **)&flash_start, (void **)&flash_end);
1373         // Keep 'end' address as last valid location, to avoid wrap around problems
1374         flash_end = (void *)((CYG_ADDRESS)flash_end - 1);
1375         flash_get_block_info(&flash_block_size, &flash_num_blocks);
1376 #ifdef CYGOPT_REDBOOT_FIS
1377         fisdir_size = CYGNUM_REDBOOT_FIS_DIRECTORY_ENTRY_COUNT * CYGNUM_REDBOOT_FIS_DIRECTORY_ENTRY_SIZE;
1378         fisdir_size = ((fisdir_size + flash_block_size - 1) / flash_block_size) * flash_block_size;
1379 # if defined(CYGPRI_REDBOOT_ZLIB_FLASH) && defined(CYGOPT_REDBOOT_FIS_ZLIB_COMMON_BUFFER)
1380         fis_work_block = fis_zlib_common_buffer;
1381         if(CYGNUM_REDBOOT_FIS_ZLIB_COMMON_BUFFER_SIZE < fisdir_size) {
1382             diag_printf("FLASH: common buffer too small\n");
1383             return false;
1384         }
1385 # else
1386         workspace_end = (unsigned char *)(workspace_end-fisdir_size);
1387         fis_work_block = workspace_end;
1388 # endif
1389         if (CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK < 0) {
1390             fis_addr = (void *)((CYG_ADDRESS)flash_end + 1 +
1391                                 (CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK*flash_block_size));
1392         } else {
1393             fis_addr = (void *)((CYG_ADDRESS)flash_start + 
1394                                 (CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK*flash_block_size));
1395         }
1396         if (((CYG_ADDRESS)fis_addr + fisdir_size - 1) > (CYG_ADDRESS)flash_end) {
1397             diag_printf("FIS directory doesn't fit\n");
1398             return false;
1399         }
1400         fis_read_directory();
1401 #endif
1402     }
1403     return true;
1404 }
1405
1406 // Wrapper to avoid compiler warnings
1407 static void
1408 _do_flash_init(void)
1409 {
1410     static int init_done = 0;
1411     if (init_done) return;
1412     init_done = 1;
1413     do_flash_init();
1414 }
1415
1416 RedBoot_init(_do_flash_init, RedBoot_INIT_FIRST);
1417
1418 static void
1419 do_fis(int argc, char *argv[])
1420 {
1421     struct cmd *cmd;
1422
1423     if (argc < 2) {
1424         fis_usage("too few arguments");
1425         return;
1426     }
1427     if (!do_flash_init()) {
1428         diag_printf("Sorry, no FLASH memory is available\n");
1429         return;
1430     }
1431     if ((cmd = cmd_search(__FIS_cmds_TAB__, &__FIS_cmds_TAB_END__, 
1432                           argv[1])) != (struct cmd *)0) {
1433         (cmd->fun)(argc, argv);
1434         return;
1435     }
1436     fis_usage("unrecognized command");
1437 }
1438
1439 // EOF flash.c