diff packages/redboot/current/src/flash.c @ 2195:5c5e93c863ac

Redundant FIS table support - from Alexander Neundorf
author gthomas
date Tue, 09 May 2006 15:52:01 +0000
parents 55e180a14a91
children eee88a59c155
line wrap: on
line diff
--- a/packages/redboot/current/src/flash.c
+++ b/packages/redboot/current/src/flash.c
@@ -169,6 +169,9 @@ int flash_block_size, flash_num_blocks;
 #ifdef CYGOPT_REDBOOT_FIS
 void *fis_work_block;
 void *fis_addr;
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+void *redundant_fis_addr;
+#endif
 int fisdir_size;  // Size of FIS directory.
 #endif
 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG
@@ -233,8 +236,8 @@ fis_lookup(char *name, int *num)
 
     img = (struct fis_image_desc *)fis_work_block;
     for (i = 0;  i < fisdir_size/sizeof(*img);  i++, img++) {
-        if ((img->name[0] != (unsigned char)0xFF) && 
-            (strcasecmp(name, img->name) == 0)) {
+        if ((img->u.name[0] != (unsigned char)0xFF) &&
+            (strcasecmp(name, img->u.name) == 0)) {
             if (num) *num = i;
             return img;
         }
@@ -242,12 +245,97 @@ fis_lookup(char *name, int *num)
     return (struct fis_image_desc *)0;
 }
 
-void
-fis_update_directory(void)
+int fis_start_update_directory(int autolock)
 {
-    int blk_size = fisdir_size;
+#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
+   // Ensure [quietly] that the directory is unlocked before trying to update and locked again afterwards
+   int do_autolock=1;
+#else
+   int do_autolock=autolock;
+#endif
+
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+   struct fis_image_desc* img=NULL;
+   void* err_addr=NULL;
+   void* tmp_fis_addr=NULL;
+
+   /*exchange old and new valid fis tables*/
+   tmp_fis_addr=fis_addr;
+   fis_addr=redundant_fis_addr;
+   redundant_fis_addr=tmp_fis_addr;
+
+   //adjust the contents of the new fis table
+   img=(struct fis_image_desc*)fis_work_block;
+
+   memcpy(img->u.valid_info.magic_name, CYG_REDBOOT_RFIS_VALID_MAGIC, CYG_REDBOOT_RFIS_VALID_MAGIC_LENGTH);
+   img->u.valid_info.valid_flag[0]=CYG_REDBOOT_RFIS_IN_PROGRESS;
+   img->u.valid_info.valid_flag[1]=CYG_REDBOOT_RFIS_IN_PROGRESS;
+   img->u.valid_info.version_count=img->u.valid_info.version_count+1;
+
+   //ready to go....
+   if (do_autolock)
+      flash_unlock((void *)fis_addr, fisdir_size, (void **)&err_addr);
+
+   flash_erase(fis_addr, fisdir_size, (void **)&err_addr);
+   //now magic is 0xffffffff
+   fis_endian_fixup(fis_work_block);
+   flash_program(fis_addr, fis_work_block, flash_block_size, (void **)&err_addr);
+   fis_endian_fixup(fis_work_block);
+   //now magic is 0xff1234ff, valid is IN_PROGRESS, version_count is the old one +1
+
+#else
+   /* nothing to do here without redundant fis */
+#endif
+   return 0;
+
+}
+
+int
+fis_update_directory(int autolock, int error)
+{
+   void* err_addr=0;
+
+#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
+   // Ensure [quietly] that the directory is unlocked before trying to update and locked again afterwards
+   int do_autolock=1;
+#else
+   int do_autolock=autolock;
+#endif
+
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+
+   struct fis_image_desc* img=(struct fis_image_desc*)fis_work_block;
+
+   // called from invalid state
+   if (img->u.valid_info.valid_flag[0]!=CYG_REDBOOT_RFIS_IN_PROGRESS)
+      return -1;
+
+   //if it failed, reset is0Valid to the state before startUpdateDirectory()
+   //g_data.fisTable hasn't been changed yet, so it doesn't have to be reset now
+   //then reread the contents from flash
+   //setting the valid flag of the failed table to "INVALID" might also be not too bad
+   //but IN_PROGRESS is also good enough I think
+   if (error!=0)
+   {
+      void* swap_fis_addr=fis_addr;
+      fis_addr=redundant_fis_addr;
+      redundant_fis_addr=swap_fis_addr;
+   }
+   else //success
+   {
+      void* tmp_fis_addr=(void *)((CYG_ADDRESS)fis_addr+CYG_REDBOOT_RFIS_VALID_MAGIC_LENGTH);
+
+      img->u.valid_info.valid_flag[0]=CYG_REDBOOT_RFIS_VALID;
+      img->u.valid_info.valid_flag[1]=CYG_REDBOOT_RFIS_VALID;
+
+      flash_program(tmp_fis_addr, img->u.valid_info.valid_flag, sizeof(img->u.valid_info.valid_flag), (void **)&err_addr);
+   }
+   if (do_autolock)
+      flash_lock((void *)fis_addr, fisdir_size, (void **)&err_addr);
+
+#else // CYGOPT_REDBOOT_REDUNDANT_FIS
+    int blk_size = fisdir_size
     int stat;
-    void *err_addr;
 
     fis_endian_fixup(fis_work_block);
 #ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
@@ -255,10 +343,9 @@ fis_update_directory(void)
     conf_endian_fixup((char *)fis_work_block+fisdir_size);
     blk_size += cfg_size;
 #endif
-#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
-    // Ensure [quietly] that the directory is unlocked before trying to update
+    if (do_autolock)
     flash_unlock((void *)fis_addr, blk_size, (void **)&err_addr);
-#endif
+
     if ((stat = flash_erase(fis_addr, blk_size, (void **)&err_addr)) != 0) {
         diag_printf("Error erasing FIS directory at %p: %s\n", err_addr, flash_errmsg(stat));
     } else {
@@ -268,12 +355,81 @@ fis_update_directory(void)
                         err_addr, flash_errmsg(stat));
         }
     }
+    fis_endian_fixup(fis_work_block);
+    if (do_autolock)
+       flash_lock((void *)fis_addr, blk_size, (void **)&err_addr);
+
+#endif // CYGOPT_REDBOOT_REDUNDANT_FIS
+
+    return 0;
+}
+
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+
+int
+fis_get_valid_buf(struct fis_image_desc* img0, struct fis_image_desc* img1, int* update_was_interrupted)
+{
+   *update_was_interrupted=0;
+   if (strncmp(img1->u.valid_info.magic_name, CYG_REDBOOT_RFIS_VALID_MAGIC, CYG_REDBOOT_RFIS_VALID_MAGIC_LENGTH)!=0)  //buf0 must be valid
+   {
+      if (img0->u.valid_info.version_count>0)
+      {
+         *update_was_interrupted=1;
+      }
+      return 0;
+   }
+   else if (strncmp(img0->u.valid_info.magic_name, CYG_REDBOOT_RFIS_VALID_MAGIC, CYG_REDBOOT_RFIS_VALID_MAGIC_LENGTH)!=0)  //buf1 must be valid
+   {
+      if (img1->u.valid_info.version_count>0)
+      {
+         *update_was_interrupted=1;
+      }
+      return 1;
+   }
+   //magic is ok for both, now check the valid flag
+   if ((img1->u.valid_info.valid_flag[0]!=CYG_REDBOOT_RFIS_VALID)
+       || (img1->u.valid_info.valid_flag[1]!=CYG_REDBOOT_RFIS_VALID)) //buf0 must be valid
+   {
+      *update_was_interrupted=1;
+      return 0;
+   }
+   else if ((img0->u.valid_info.valid_flag[0]!=CYG_REDBOOT_RFIS_VALID)
+            || (img0->u.valid_info.valid_flag[1]!=CYG_REDBOOT_RFIS_VALID)) //buf1 must be valid
+   {
+      *update_was_interrupted=1;
+      return 1;
+   }
+
+   //now check the version
+   if (img1->u.valid_info.version_count == (img0->u.valid_info.version_count+1)) //buf1 must be valid
+      return 1;
+
+   return 0;
+}
+
+void
+fis_erase_redundant_directory(void)
+{
+    int stat;
+    void *err_addr;
+
+#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
+    // Ensure [quietly] that the directory is unlocked before trying
+    // to update
+    flash_unlock((void *)redundant_fis_addr, fisdir_size,
+                 (void **)&err_addr);
+#endif
+    if ((stat = flash_erase(redundant_fis_addr, fisdir_size,
+                            (void **)&err_addr)) != 0) {
+         diag_printf("Error erasing FIS directory at %p: %s\n",
+                     err_addr, flash_errmsg(stat));
+    }
 #ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
     // Ensure [quietly] that the directory is locked after the update
-    flash_lock((void *)fis_addr, blk_size, (void **)&err_addr);
+    flash_lock((void *)redundant_fis_addr, fisdir_size, (void **)&err_addr);
 #endif
-    fis_endian_fixup(fis_work_block);
 }
+#endif
 
 static void
 fis_init(int argc, char *argv[])
@@ -303,12 +459,23 @@ fis_init(int argc, char *argv[])
     redboot_image_size = flash_block_size > MIN_REDBOOT_IMAGE_SIZE ? 
                          flash_block_size : MIN_REDBOOT_IMAGE_SIZE;
 
-    // Create a pseudo image for RedBoot
     img = (struct fis_image_desc *)fis_work_block;
     memset(img, 0xFF, fisdir_size);  // Start with erased data
+
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+    //create the valid flag entry
+    memset(img, 0, sizeof(struct fis_image_desc));
+    strcpy(img->u.valid_info.magic_name, CYG_REDBOOT_RFIS_VALID_MAGIC);
+    img->u.valid_info.valid_flag[0]=CYG_REDBOOT_RFIS_VALID;
+    img->u.valid_info.valid_flag[1]=CYG_REDBOOT_RFIS_VALID;
+    img->u.valid_info.version_count=0;
+    img++;
+#endif
+
+    // Create a pseudo image for RedBoot
 #ifdef CYGOPT_REDBOOT_FIS_RESERVED_BASE
     memset(img, 0, sizeof(*img));
-    strcpy(img->name, "(reserved)");
+    strcpy(img->u.name, "(reserved)");
     img->flash_base = (CYG_ADDRESS)flash_start;
     img->mem_base = (CYG_ADDRESS)flash_start;
     img->size = CYGNUM_REDBOOT_FLASH_RESERVED_BASE;
@@ -317,7 +484,7 @@ fis_init(int argc, char *argv[])
     redboot_flash_start = (CYG_ADDRESS)flash_start + CYGBLD_REDBOOT_FLASH_BOOT_OFFSET;
 #ifdef CYGOPT_REDBOOT_FIS_REDBOOT
     memset(img, 0, sizeof(*img));
-    strcpy(img->name, "RedBoot");
+    strcpy(img->u.name, "RedBoot");
     img->flash_base = redboot_flash_start;
     img->mem_base = redboot_flash_start;
     img->size = redboot_image_size;
@@ -330,7 +497,7 @@ fis_init(int argc, char *argv[])
     redboot_flash_start = (CYG_ADDRESS)flash_start + CYGNUM_REDBOOT_FIS_REDBOOT_POST_OFFSET;
 #endif
     memset(img, 0, sizeof(*img));
-    strcpy(img->name, "RedBoot[post]");
+    strcpy(img->u.name, "RedBoot[post]");
     img->flash_base = redboot_flash_start;
     img->mem_base = redboot_flash_start;
     img->size = redboot_image_size;
@@ -340,7 +507,7 @@ fis_init(int argc, char *argv[])
 #ifdef CYGOPT_REDBOOT_FIS_REDBOOT_BACKUP
     // And a backup image
     memset(img, 0, sizeof(*img));
-    strcpy(img->name, "RedBoot[backup]");
+    strcpy(img->u.name, "RedBoot[backup]");
     img->flash_base = redboot_flash_start;
     img->mem_base = redboot_flash_start;
     img->size = redboot_image_size;
@@ -350,7 +517,7 @@ fis_init(int argc, char *argv[])
 #if defined(CYGSEM_REDBOOT_FLASH_CONFIG) && defined(CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH)
     // And a descriptor for the configuration data
     memset(img, 0, sizeof(*img));
-    strcpy(img->name, "RedBoot config");
+    strcpy(img->u.name, "RedBoot config");
     img->flash_base = (CYG_ADDRESS)cfg_base;
     img->mem_base = (CYG_ADDRESS)cfg_base;
     img->size = cfg_size;
@@ -358,12 +525,22 @@ fis_init(int argc, char *argv[])
 #endif
     // And a descriptor for the descriptor table itself
     memset(img, 0, sizeof(*img));
-    strcpy(img->name, "FIS directory");
+    strcpy(img->u.name, "FIS directory");
     img->flash_base = (CYG_ADDRESS)fis_addr;
     img->mem_base = (CYG_ADDRESS)fis_addr;
     img->size = fisdir_size;
     img++;
 
+    //create the entry for the redundant fis table
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+    memset(img, 0, sizeof(*img));
+    strcpy(img->u.name, "Redundant FIS");
+    img->flash_base = (CYG_ADDRESS)redundant_fis_addr;
+    img->mem_base = (CYG_ADDRESS)redundant_fis_addr;
+    img->size = fisdir_size;
+    img++;
+#endif
+
 #ifdef CYGOPT_REDBOOT_FIS_DIRECTORY_ARM_SIB_ID
     // FIS gets the size of a full block - note, this should be changed
     // if support is added for multi-block FIS structures.
@@ -476,7 +653,11 @@ fis_init(int argc, char *argv[])
         diag_printf("    Warning: device contents not erased, some blocks may not be usable\n");
 #endif
     }
-    fis_update_directory();
+    fis_start_update_directory(0);
+    fis_update_directory(0, 0);
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+    fis_erase_redundant_directory();
+#endif
 }
 
 static void
@@ -525,7 +706,7 @@ fis_list(int argc, char *argv[])
         lowest_addr = 0xFFFFFFFF;
         img = (struct fis_image_desc *) fis_work_block;
         for (i = 0;  i < fisdir_size/sizeof(*img);  i++, img++) {
-            if (img->name[0] != (unsigned char)0xFF) {
+            if (img->u.name[0] != (unsigned char)0xFF) {
                 if ((img->flash_base > last_addr) && (img->flash_base < lowest_addr)) {
                     lowest_addr = img->flash_base;
                     image_found = true;
@@ -536,7 +717,7 @@ fis_list(int argc, char *argv[])
         if (image_found) {
             img = (struct fis_image_desc *) fis_work_block;
             img += image_indx;
-            diag_printf("%-16s  0x%08lX  0x%08lX  0x%08lX  0x%08lX\n", img->name, 
+            diag_printf("%-16s  0x%08lX  0x%08lX  0x%08lX  0x%08lX\n", img->u.name,
                         (unsigned long)img->flash_base, 
 #ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
                         show_cksums ? img->file_cksum : img->mem_base, 
@@ -573,7 +754,7 @@ find_free(struct free_chunk *chunks)
     fis_read_directory();
     img = (struct fis_image_desc *) fis_work_block;
     for (i = 0;  i < fisdir_size/sizeof(*img);  i++, img++) {
-        if (img->name[0] != (unsigned char)0xFF) {
+        if (img->u.name[0] != (unsigned char)0xFF) {
             // Figure out which chunk this is in and split it
             for (idx = 0;  idx < num_chunks;  idx++) {
                 if ((img->flash_base >= chunks[idx].start) && 
@@ -841,8 +1022,8 @@ fis_create(int argc, char *argv[])
         diag_printf("   must be 0x%x aligned\n", flash_block_size);
         return;
     }
-    if (strlen(name) >= sizeof(img->name)) {
-        diag_printf("Name is too long, must be less than %d chars\n", (int)sizeof(img->name));
+    if (strlen(name) >= sizeof(img->u.name)) {
+        diag_printf("Name is too long, must be less than %d chars\n", (int)sizeof(img->u.name));
         return;
     }
     if (!no_copy) {
@@ -907,7 +1088,7 @@ fis_create(int argc, char *argv[])
         // If not image by that name, try and find an empty slot
         img = (struct fis_image_desc *)fis_work_block;
         for (i = 0;  i < fisdir_size/sizeof(*img);  i++, img++) {
-            if (img->name[0] == (unsigned char)0xFF) {
+            if (img->u.name[0] == (unsigned char)0xFF) {
                 break;
             }
         }
@@ -940,7 +1121,7 @@ fis_create(int argc, char *argv[])
     if (prog_ok) {
         // Update directory
         memset(img, 0, sizeof(*img));
-        strcpy(img->name, name);
+        strcpy(img->u.name, name);
         img->flash_base = flash_addr;
         img->mem_base = exec_addr_set ? exec_addr : (mem_addr_set ? mem_addr : flash_addr);
         img->entry_point = entry_addr_set ? entry_addr : (CYG_ADDRESS)entry_address;  // Hope it's been set
@@ -954,7 +1135,8 @@ fis_create(int argc, char *argv[])
             img->file_cksum = 0;
         }
 #endif
-        fis_update_directory();
+        fis_start_update_directory(0);
+        fis_update_directory(0, 0);
     }
 }
 
@@ -1001,7 +1183,7 @@ fis_delete(int argc, char *argv[])
     img = fis_lookup(name, &i);
     if (img) {
         if (i < num_reserved) {
-            diag_printf("Sorry, '%s' is a reserved image and cannot be deleted\n", img->name);
+            diag_printf("Sorry, '%s' is a reserved image and cannot be deleted\n", img->u.name);
             return;
         }
         if (!verify_action("Delete image '%s'", name)) {
@@ -1015,8 +1197,9 @@ fis_delete(int argc, char *argv[])
     if ((stat = flash_erase((void *)img->flash_base, img->size, (void **)&err_addr)) != 0) {
         diag_printf("Error erasing at %p: %s\n", err_addr, flash_errmsg(stat));
     } else {
-        img->name[0] = (unsigned char)0xFF;    
-        fis_update_directory();
+        img->u.name[0] = (unsigned char)0xFF;    
+        fis_start_update_directory(0);
+        fis_update_directory(0, 0);
     }
 }
 
@@ -1364,16 +1547,32 @@ void
            flash_start, (CYG_ADDRWORD)flash_end + 1, flash_num_blocks, (void *)flash_block_size);
 }
 
-bool
+/* Returns -1 on failure, 0 on success, 1 if it was successfull
+ but a failed fis update was detected  */
+int
 do_flash_init(void)
 {
     int stat;
 
+    void *err_addr;
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+    struct fis_image_desc img0;
+    struct fis_image_desc img1;
+    int fis_update_was_interrupted=0;
+
+    //check the size of fis_valid_info
+    CYG_ASSERT((sizeof(struct fis_valid_info)<=sizeof(img0.u.name)), "fis_valid_info size mismatch");
+    //try to check the alignment of version_count
+    CYG_ASSERT((((unsigned long)&img0.u.valid_info.version_count - (unsigned long)&img0) % sizeof(unsigned long) == 0), "alignment problem");
+#endif
+
+
+
     if (!__flash_init) {
         __flash_init = 1;
         if ((stat = flash_init(diag_printf)) != 0) {
             diag_printf("FLASH: driver init failed: %s\n", flash_errmsg(stat));
-            return false;
+            return -1;
         }
         flash_get_limits((void *)0, (void **)&flash_start, (void **)&flash_end);
         // Keep 'end' address as last valid location, to avoid wrap around problems
@@ -1386,10 +1585,10 @@ do_flash_init(void)
 	fis_work_block = fis_zlib_common_buffer;
 	if(CYGNUM_REDBOOT_FIS_ZLIB_COMMON_BUFFER_SIZE < fisdir_size) {
             diag_printf("FLASH: common buffer too small\n");
-            return false;
+            return -1;
 	}
 # else
-        workspace_end = (unsigned char *)(workspace_end-fisdir_size);
+        workspace_end = (unsigned char *)(workspace_end_init-fisdir_size);
         fis_work_block = workspace_end;
 # endif
         if (CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK < 0) {
@@ -1402,12 +1601,60 @@ do_flash_init(void)
         
         if (((CYG_ADDRESS)fis_addr + fisdir_size - 1) > (CYG_ADDRESS)flash_end) {
             diag_printf("FIS directory doesn't fit\n");
-            return false;
+            return -1;
+        }
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+
+        if (CYGNUM_REDBOOT_FIS_REDUNDANT_DIRECTORY_BLOCK < 0) {
+            redundant_fis_addr = (void *)((CYG_ADDRESS)flash_end + 1 +
+                                          (CYGNUM_REDBOOT_FIS_REDUNDANT_DIRECTORY_BLOCK*flash_block_size));
+        } else {
+            redundant_fis_addr = (void *)((CYG_ADDRESS)flash_start +
+                                (CYGNUM_REDBOOT_FIS_REDUNDANT_DIRECTORY_BLOCK*flash_block_size));
+        }
+
+        if (((CYG_ADDRESS)redundant_fis_addr + fisdir_size - 1) > (CYG_ADDRESS)flash_end) {
+            diag_printf("Redundant FIS directory doesn't fit\n");
+            return -1;
         }
+        FLASH_READ(fis_addr, &img0, sizeof(img0), (void **)&err_addr);
+        FLASH_READ(redundant_fis_addr, &img1, sizeof(img1), (void **)&err_addr);
+
+        if (strncmp(img0.u.valid_info.magic_name, CYG_REDBOOT_RFIS_VALID_MAGIC, CYG_REDBOOT_RFIS_VALID_MAGIC_LENGTH)!=0)
+        {
+           memset(&img0, 0, sizeof(img0));
+        }
+
+        if (strncmp(img1.u.valid_info.magic_name, CYG_REDBOOT_RFIS_VALID_MAGIC, CYG_REDBOOT_RFIS_VALID_MAGIC_LENGTH)!=0)
+        {
+           memset(&img1, 0, sizeof(img0));
+        }
+
+#ifdef REDBOOT_FLASH_REVERSE_BYTEORDER
+        img0.u.valid_info.version_count = CYG_SWAP32(img0.u.valid_info.version_count);
+        img1.u.valid_info.version_count = CYG_SWAP32(img1.u.valid_info.version_count);
+#endif
+
+        if (fis_get_valid_buf(&img0, &img1, &fis_update_was_interrupted)==1)
+        {
+           // Valid, so swap primary and secondary
+           void * tmp;
+           tmp = fis_addr;
+           fis_addr = redundant_fis_addr;
+           redundant_fis_addr = tmp;
+        }
+#endif
         fis_read_directory();
 #endif
     }
-    return true;
+#ifdef CYGOPT_REDBOOT_REDUNDANT_FIS
+    if (fis_update_was_interrupted)
+       return 1;
+    else
+       return 0;
+#else
+    return 0;
+#endif
 }
 
 // Wrapper to avoid compiler warnings
@@ -1431,7 +1678,7 @@ do_fis(int argc, char *argv[])
         fis_usage("too few arguments");
         return;
     }
-    if (!do_flash_init()) {
+    if (do_flash_init()<0) {
         diag_printf("Sorry, no FLASH memory is available\n");
         return;
     }