This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new 0639a2ad7 boot/nxboot: enhance bootloader capabilities and decision 
logic
0639a2ad7 is described below

commit 0639a2ad7ba473463923ce377b0227763bf051f9
Author: Michal Lenc <michall...@seznam.cz>
AuthorDate: Mon Mar 10 13:16:25 2025 +0100

    boot/nxboot: enhance bootloader capabilities and decision logic
    
    This commit enhances the bootloader capabilities. The image's header
    is extended with header version, size, platform identifier and
    pointer to optional next header. CRC32 now includes part of
    the header in its calculation as well.
    
    The change also avoids having two different magics for image uploaded
    over programmer and update image. Both these images have the same
    magic and this magic is changed internally by the bootloader's logic.
    The change is needed because image with standard magic is automatically
    considered as a confirmed image (uploaded with programmer).
    
    The current implementation avoids tails at all, therefore the user
    application uploading the image does not have to erase the tail before
    new upload. The image is considered as confirmed if it has standard
    magic or its recovery is present. This means the bootloader has to
    erase the header of the update image after the update is done (to
    avoid update loop and to mark the image as unstable). This page is
    written back during the confirmation.
    
    This is a breaking change, but necessary for the future development
    of the bootloader. The added header version field will allow to
    add minor/major updates while keeping the backwards compatibility.
    
    Signed-off-by: Michal Lenc <michall...@seznam.cz>
    Co-authored-by: Pavel Pisa <p...@fel.cvut.cz>
    Co-authored-by: Karel Koci <cyn...@email.cz>
---
 boot/nxboot/Kconfig          |  11 +-
 boot/nxboot/include/nxboot.h |  86 ++++++--
 boot/nxboot/loader/boot.c    | 499 +++++++++++++++++++++++++------------------
 boot/nxboot/loader/flash.c   |  15 +-
 boot/nxboot/loader/flash.h   |   7 +-
 boot/nxboot/tools/nximage.py |  62 +++---
 6 files changed, 409 insertions(+), 271 deletions(-)

diff --git a/boot/nxboot/Kconfig b/boot/nxboot/Kconfig
index cbf596a18..f8e817b5a 100644
--- a/boot/nxboot/Kconfig
+++ b/boot/nxboot/Kconfig
@@ -43,6 +43,15 @@ config NXBOOT_HEADER_SIZE
                Note that this size should be aligned with the program memory 
write
                page size!
 
+config NXBOOT_PLATFORM_IDENTIFIER
+       hex "A unique platform identifier"
+       default 0x0
+       ---help---
+               This is a unique platform identifier used by the bootloader to
+               verify whether the image should be run on a given platform. An 
update
+               (or even a firmware uploaded via a programmer) is rejected if 
the
+               value in image's header doesn't match this option.
+
 config NXBOOT_BOOTLOADER
        bool "Build nxboot bootloader application"
        default n
@@ -80,7 +89,7 @@ config NXBOOT_PREVENT_DOWNGRADE
                performed only for newer versions  (according to Semantic 
Version
                preference rules).
 
-               WARNING: NXboot currently implementes preferences only for
+               WARNING: NXboot currently implements preferences only for
                MAJOR.MINOR.PATCH and ignores prerelease.
 
 endif # NXBOOT_BOOTLOADER
diff --git a/boot/nxboot/include/nxboot.h b/boot/nxboot/include/nxboot.h
index 2ecf961c7..5370c2ee6 100644
--- a/boot/nxboot/include/nxboot.h
+++ b/boot/nxboot/include/nxboot.h
@@ -39,26 +39,32 @@
 #define NXBOOT_SECONDARY_SLOT_NUM (1)
 #define NXBOOT_TERTIARY_SLOT_NUM  (2)
 
-/* Offsets to write pages containing confirmed and updated flags. These
- * pages are located at the end of the partition, therefore index 0 means
- * the first page from the end.
- */
-
-#define NXBOOT_CONFIRMED_PAGE_INDEX (0)
-#define NXBOOT_UPDATED_PAGE_INDEX   (1)
-
-#define NXBOOT_HEADER_MAGIC     0x534f584e /* NXOS. */
-#define NXBOOT_HEADER_MAGIC_INV 0xaca0abb1 /* NXOS inverted. This is used
-                                            * for images uploaded directly
-                                            * to the primary flash with
-                                            * the debugger. These images
-                                            * does not have precalculated
-                                            * CRC and flags at the
-                                            * end of the partition, but
-                                            * are considered to be valid.
+#define NXBOOT_HEADER_MAGIC     0x534f584e /* NXOS. The NX images, both
+                                            * uploaded directly to primary
+                                            * partition via debugger and to
+                                            * update via some application
+                                            * are used with this magic. If
+                                            * this image is uploaded to
+                                            * primary flash, it is considered
+                                            * valid.
                                             */
+#define NXBOOT_HEADER_MAGIC_INT 0xaca0abb0 /* NXOS internal. This is used
+                                            * for internal bootloader
+                                            * handling and operations. It is
+                                            * switch internally to distinguish
+                                            * between images uploaded via
+                                            * debugger or the ones updated
+                                            * after the bootloader performed
+                                            * its operation. The first two
+                                            * bits are reserved to point
+                                            * what partition is a recovery
+                                            * for this image.
+                                            */
+
+#define NXBOOT_HEADER_MAGIC_INT_MASK 0xfffffff0
+#define NXBOOT_RECOVERY_PTR_MASK 0x3
 
-#define NXBOOT_HEADER_PRERELEASE_MAXLEN 110
+#define NXBOOT_HEADER_PRERELEASE_MAXLEN 94
 
 /****************************************************************************
  * Public Types
@@ -84,11 +90,30 @@ struct nxboot_img_version
   char pre_release[NXBOOT_HEADER_PRERELEASE_MAXLEN];  /* Additional 
pre-release version */
 };
 
+struct nxboot_hdr_version
+{
+  uint8_t major;
+  uint8_t minor;
+};
+
 struct nxboot_img_header
 {
-  uint32_t magic;  /* Header magic */
-  uint32_t size;   /* Image size (excluding the header) */
-  uint32_t crc;    /* CRC32 of image (excluding the header). */
+  uint32_t magic;                         /* Header magic */
+  struct nxboot_hdr_version hdr_version;  /* Version of the header */
+
+  uint16_t header_size;     /* Length of the header in bytes */
+  uint32_t crc;             /* CRC32 of image (excluding the previous
+                             * fields in header, but including the following
+                             * ones).
+                             */
+  uint32_t size;            /* Image size (excluding the header) */
+  uint64_t identifier;      /* Platform identifier. An image is rejected
+                             * if it does not match the one set for
+                             * the bootloader in NXBOOT_PLATFORM_IDENTIFIER.
+                             */
+  uint32_t extd_hdr_ptr;    /* Address of the next extended header.
+                             * This is a hook for future additional headers.
+                             */
 
   struct nxboot_img_version img_version; /* Image version */
 };
@@ -101,8 +126,9 @@ struct nxboot_state
   int update;                         /* Number of update slot */
   int recovery;                       /* Number of recovery slot */
   bool recovery_valid;                /* True if recovery image contains valid 
recovery */
+  bool recovery_present;              /* True if the image in primary has a 
recovery */
   bool primary_confirmed;             /* True if primary slot is confirmed */
-  enum nxboot_update_type next_boot;  /* True if update slot has a valid image 
*/
+  enum nxboot_update_type next_boot;  /* nxboot_update_type with next 
operation */
 };
 
 /****************************************************************************
@@ -128,6 +154,22 @@ struct nxboot_state
 
 int nxboot_get_state(struct nxboot_state *state);
 
+/****************************************************************************
+ * Name: nxboot_open_update_partition
+ *
+ * Description:
+ *   Gets the current bootloader state and opens the partition to which an
+ *   update image should be stored. It returns the valid file descriptor to
+ *   this partition, the user is responsible for writing to it and closing
+ *   if afterwards.
+ *
+ * Returned Value:
+ *   Valid file descriptor on success, -1 and sets errno on failure.
+ *
+ ****************************************************************************/
+
+int nxboot_open_update_partition(void);
+
 /****************************************************************************
  * Name: nxboot_get_confirm
  *
diff --git a/boot/nxboot/loader/boot.c b/boot/nxboot/loader/boot.c
index ec1ec3c65..9a43f4cb6 100644
--- a/boot/nxboot/loader/boot.c
+++ b/boot/nxboot/loader/boot.c
@@ -45,44 +45,13 @@
   # warning "Downgrade prevention currently ignores prerelease."
 #endif
 
+#define IS_INTERNAL_MAGIC(magic) ((magic & NXBOOT_HEADER_MAGIC_INT_MASK) \
+                                  == NXBOOT_HEADER_MAGIC_INT)
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
 
-static inline bool get_image_flag(int fd, int index)
-{
-  uint8_t flag;
-  struct flash_partition_info info;
-
-  if (flash_partition_info(fd, &info) < 0)
-    {
-      return false;
-    }
-
-  if (flash_partition_read(fd, &flag, 1,
-                           info.size - info.blocksize * (index + 1)) < 0)
-    {
-      return false;
-    }
-
-  return flag == 0xfe;
-}
-
-static inline int set_image_flag(int fd, int index)
-{
-  uint8_t flag;
-  struct flash_partition_info info;
-
-  if (flash_partition_info(fd, &info) < 0)
-    {
-      return ERROR;
-    }
-
-  flag = 0xfe;
-  return flash_partition_write(fd, &flag, 1,
-                               info.size - (info.blocksize * (index + 1)));
-}
-
 static inline void get_image_header(int fd, struct nxboot_img_header *header)
 {
   int ret;
@@ -97,8 +66,9 @@ static inline void get_image_header(int fd, struct 
nxboot_img_header *header)
 
 static inline bool validate_image_header(struct nxboot_img_header *header)
 {
-  return header->magic == NXBOOT_HEADER_MAGIC ||
-         header->magic == NXBOOT_HEADER_MAGIC_INV;
+  return (header->magic == NXBOOT_HEADER_MAGIC ||
+         IS_INTERNAL_MAGIC(header->magic)) &&
+         header->identifier == CONFIG_NXBOOT_PLATFORM_IDENTIFIER;
 }
 
 static uint32_t calculate_crc(int fd, struct nxboot_img_header *header)
@@ -122,8 +92,8 @@ static uint32_t calculate_crc(int fd, struct 
nxboot_img_header *header)
     }
 
   crc = 0xffffffff;
-  remain = header->size;
-  off = CONFIG_NXBOOT_HEADER_SIZE;
+  off = offsetof(struct nxboot_img_header, crc) + sizeof crc;
+  remain = header->size + header->header_size - off;
   while (remain > 0)
     {
       readsiz = remain > info.blocksize ? info.blocksize : remain;
@@ -142,12 +112,12 @@ static uint32_t calculate_crc(int fd, struct 
nxboot_img_header *header)
   return ~crc;
 }
 
-static int copy_partition(int from, int where)
+static int copy_partition(int from, int where, struct nxboot_state *state,
+                          bool update)
 {
   struct nxboot_img_header header;
   struct flash_partition_info info_from;
   struct flash_partition_info info_where;
-  uint32_t crc;
   uint32_t magic;
   int readsiz;
   int remain;
@@ -167,7 +137,8 @@ static int copy_partition(int from, int where)
       return ERROR;
     }
 
-  if ((header.size + CONFIG_NXBOOT_HEADER_SIZE) > info_where.size)
+  remain = header.size + header.header_size;
+  if (remain > info_where.size)
     {
       return ERROR;
     }
@@ -180,43 +151,40 @@ static int copy_partition(int from, int where)
       return ERROR;
     }
 
-  if (flash_partition_erase_last_sector(where) < 0)
-    {
-      return ERROR;
-    }
+  /* Flip header's magic. We go from standard to internal in case of
+   * image update and from internal to standard in case of recovery
+   * creation or revert operation.
+   */
 
-  remain = header.size + CONFIG_NXBOOT_HEADER_SIZE;
-  off = 0;
-  if (header.magic == NXBOOT_HEADER_MAGIC_INV)
+  magic = IS_INTERNAL_MAGIC(header.magic) ?
+    NXBOOT_HEADER_MAGIC : NXBOOT_HEADER_MAGIC_INT;
+
+  if (update)
     {
-      /* This means we are doing a recovery of a primary image
-       * without the precalculated CRC. Calculate CRC and insert it
-       * into the recovery image header. Also flip header's magic to
-       * indicate this is an image with valid CRC.
+      /* This is update operation, add pointer to recovery image to the
+       * image's magic.
        */
 
-      magic = NXBOOT_HEADER_MAGIC;
-      crc = calculate_crc(from, &header);
-      if (flash_partition_read(from, buf, blocksize, 0) < 0)
-        {
-          free(buf);
-          return ERROR;
-        }
+      magic |= state->update;
+    }
 
-      memcpy(buf + offsetof(struct nxboot_img_header, magic), &magic,
-             sizeof magic);
-      memcpy(buf + offsetof(struct nxboot_img_header, crc), &crc,
-             sizeof crc);
-      if (flash_partition_write(where, buf, blocksize, 0) < 0)
-        {
-          free(buf);
-          return ERROR;
-        }
+  if (flash_partition_read(from, buf, blocksize, 0) < 0)
+    {
+      free(buf);
+      return ERROR;
+    }
 
-      off += blocksize;
-      remain -= blocksize;
+  memcpy(buf + offsetof(struct nxboot_img_header, magic), &magic,
+          sizeof magic);
+  if (flash_partition_write(where, buf, blocksize, 0) < 0)
+    {
+      free(buf);
+      return ERROR;
     }
 
+  off = blocksize;
+  remain -= blocksize;
+
   while (remain > 0)
     {
       readsiz = remain > blocksize ? blocksize : remain;
@@ -236,27 +204,6 @@ static int copy_partition(int from, int where)
       remain -= readsiz;
     }
 
-  if (header.magic != NXBOOT_HEADER_MAGIC_INV)
-    {
-      /* Copy currently set flags but only if the image has
-       * precalculated CRC.
-       */
-
-      if (get_image_flag(from, NXBOOT_UPDATED_PAGE_INDEX))
-        {
-          set_image_flag(where, NXBOOT_UPDATED_PAGE_INDEX);
-        }
-
-      if (get_image_flag(from, NXBOOT_CONFIRMED_PAGE_INDEX))
-        {
-          set_image_flag(where, NXBOOT_CONFIRMED_PAGE_INDEX);
-        }
-    }
-  else
-    {
-      set_image_flag(where, NXBOOT_CONFIRMED_PAGE_INDEX);
-    }
-
   free(buf);
   return OK;
 }
@@ -271,21 +218,7 @@ static bool validate_image(int fd)
       return false;
     }
 
-  if (header.magic == NXBOOT_HEADER_MAGIC_INV)
-    {
-      /* Images with no precalculated CRC are considered valid. These
-       * should be the images that are uploaded directly to the primary
-       * paritition with debugger/flasher and are not uploaded by the
-       * bootloader. These images also don't have confirmed flags,
-       * altough they are considered stable.
-       */
-
-      return true;
-    }
-  else
-    {
-      return calculate_crc(fd, &header) == header.crc;
-    }
+  return calculate_crc(fd, &header) == header.crc;
 }
 
 static bool compare_versions(struct nxboot_img_version *v1,
@@ -329,31 +262,29 @@ static bool compare_versions(struct nxboot_img_version 
*v1,
 }
 
 static enum nxboot_update_type
-  get_update_type(int primary, int update, int recovery,
+  get_update_type(struct nxboot_state *state,
+                  int primary, int update, int recovery,
                   struct nxboot_img_header *primary_header,
-                  struct nxboot_img_header *update_header)
+                  struct nxboot_img_header *update_header,
+                  struct nxboot_img_header *recovery_header)
 {
-  if (get_image_flag(recovery, NXBOOT_CONFIRMED_PAGE_INDEX) &&
-      get_image_flag(update, NXBOOT_UPDATED_PAGE_INDEX) &&
-      ((!get_image_flag(primary, NXBOOT_CONFIRMED_PAGE_INDEX) &&
-      primary_header->magic != NXBOOT_HEADER_MAGIC_INV) ||
-      !validate_image(primary)) && validate_image(recovery))
-    {
-      return NXBOOT_UPDATE_TYPE_REVERT;
-    }
+  bool primary_valid = validate_image(primary);
 
-  if (!get_image_flag(update, NXBOOT_CONFIRMED_PAGE_INDEX) &&
-      !get_image_flag(update, NXBOOT_UPDATED_PAGE_INDEX) &&
-      validate_image(update))
+  if (update_header->magic == NXBOOT_HEADER_MAGIC && validate_image(update))
     {
-      if (compare_versions(&primary_header->img_version,
-                           &update_header->img_version) &&
-          validate_image(primary))
+      if (primary_header->crc != update_header->crc ||
+          !compare_versions(&primary_header->img_version,
+          &update_header->img_version) || !primary_valid)
         {
-          return NXBOOT_UPDATE_TYPE_NONE;
+          return NXBOOT_UPDATE_TYPE_UPDATE;
         }
+    }
 
-      return NXBOOT_UPDATE_TYPE_UPDATE;
+  if (IS_INTERNAL_MAGIC(recovery_header->magic) && state->recovery_valid &&
+      ((IS_INTERNAL_MAGIC(primary_header->magic) &&
+      !state->primary_confirmed) || !primary_valid))
+    {
+      return NXBOOT_UPDATE_TYPE_REVERT;
     }
 
   return NXBOOT_UPDATE_TYPE_NONE;
@@ -369,13 +300,25 @@ static int perform_update(struct nxboot_state *state, 
bool check_only)
   bool primary_valid;
 
   primary = flash_partition_open(CONFIG_NXBOOT_PRIMARY_SLOT_PATH);
-  assert(primary >= 0);
+  if (primary < 0)
+    {
+      return ERROR;
+    }
 
   secondary = flash_partition_open(CONFIG_NXBOOT_SECONDARY_SLOT_PATH);
-  assert(secondary >= 0);
+  if (secondary < 0)
+    {
+      flash_partition_close(primary);
+      return ERROR;
+    }
 
   tertiary = flash_partition_open(CONFIG_NXBOOT_TERTIARY_SLOT_PATH);
-  assert(tertiary >= 0);
+  if (tertiary < 0)
+    {
+      flash_partition_close(primary);
+      flash_partition_close(secondary);
+      return ERROR;
+    }
 
   if (state->update == NXBOOT_SECONDARY_SLOT_NUM)
     {
@@ -391,10 +334,10 @@ static int perform_update(struct nxboot_state *state, 
bool check_only)
   if (state->next_boot == NXBOOT_UPDATE_TYPE_REVERT &&
       (!check_only || !validate_image(primary)))
     {
-      if (validate_image(recovery))
+      if (state->recovery_valid)
         {
           syslog(LOG_INFO, "Reverting image to recovery.\n");
-          copy_partition(recovery, primary);
+          copy_partition(recovery, primary, state, false);
         }
     }
   else
@@ -409,8 +352,8 @@ static int perform_update(struct nxboot_state *state, bool 
check_only)
           goto perform_update_done;
         }
 
-      if (!state->recovery_valid && state->primary_confirmed &&
-          primary_valid)
+      if ((!state->recovery_present || !state->recovery_valid) &&
+          state->primary_confirmed && primary_valid)
         {
           /* Save current image as recovery only if it is valid and
            * confirmed. We have to check this in case of restart
@@ -425,10 +368,10 @@ static int perform_update(struct nxboot_state *state, 
bool check_only)
            */
 
           syslog(LOG_INFO, "Creating recovery image.\n");
-          copy_partition(primary, recovery);
+          copy_partition(primary, recovery, state, false);
           if (!validate_image(recovery))
             {
-              syslog(LOG_INFO, "New recovery is not valid, stop update\n");
+              syslog(LOG_INFO, "New recovery is not valid, stop update.\n");
               goto perform_update_done;
             }
 
@@ -440,13 +383,16 @@ static int perform_update(struct nxboot_state *state, 
bool check_only)
           /* Perform update only if update slot contains valid image. */
 
           syslog(LOG_INFO, "Updating from update image.\n");
-          copy_partition(update, primary);
-
-          /* Mark update slot as updated. This is to prevent repeated
-           * updates.
-           */
+          if (copy_partition(update, primary, state, true) >= 0)
+            {
+              /* Erase the first sector of update partition. This marks the
+               * partition as updated so we don't end up in an update loop.
+               * The sector is written back again during the image
+               * confirmation.
+               */
 
-          set_image_flag(update, NXBOOT_UPDATED_PAGE_INDEX);
+              flash_partition_erase_first_sector(update);
+            }
         }
     }
 
@@ -485,10 +431,12 @@ int nxboot_get_state(struct nxboot_state *state)
   int tertiary;
   int update;
   int recovery;
+  int recovery_pointer;
   struct nxboot_img_header primary_header;
   struct nxboot_img_header secondary_header;
   struct nxboot_img_header tertiary_header;
   struct nxboot_img_header *update_header;
+  struct nxboot_img_header *recovery_header;
 
   memset(state, 0, sizeof *state);
 
@@ -517,44 +465,98 @@ int nxboot_get_state(struct nxboot_state *state)
   get_image_header(secondary, &secondary_header);
   get_image_header(tertiary, &tertiary_header);
 
+  /* Determine which partition is for update and which is a recovery.
+   * This depends on many factors, but in general a partition with
+   * NXBOOT_HEADER_MAGIC is an update partition and partition with
+   * NXBOOT_HEADER_MAGIC_INT is a recovery.
+   *
+   * A special case is when both partitions have NXBOOT_HEADER_MAGIC_INT,
+   * then we look into a recovery pointer in primary header magic and
+   * determine the recovery from it.
+   */
+
   update = secondary;
   recovery = tertiary;
   update_header = &secondary_header;
+  recovery_header = &tertiary_header;
   state->update = NXBOOT_SECONDARY_SLOT_NUM;
   state->recovery = NXBOOT_TERTIARY_SLOT_NUM;
-  if (get_image_flag(secondary, NXBOOT_CONFIRMED_PAGE_INDEX) &&
-      validate_image(secondary))
-    {
-      /* Secondary image is confirmed and valid, use this as
-       * a potential recovery.
-       */
 
+  if (tertiary_header.magic == NXBOOT_HEADER_MAGIC)
+    {
       update = tertiary;
       recovery = secondary;
+      update_header = &tertiary_header;
+      recovery_header = &secondary_header;
       state->recovery = NXBOOT_SECONDARY_SLOT_NUM;
       state->update = NXBOOT_TERTIARY_SLOT_NUM;
-      update_header = &tertiary_header;
-
-      if (secondary_header.crc == primary_header.crc)
+    }
+  else if (IS_INTERNAL_MAGIC(secondary_header.magic) &&
+           IS_INTERNAL_MAGIC(tertiary_header.magic))
+    {
+      if (IS_INTERNAL_MAGIC(primary_header.magic))
+        {
+          recovery_pointer = primary_header.magic & NXBOOT_RECOVERY_PTR_MASK;
+          if (recovery_pointer == NXBOOT_SECONDARY_SLOT_NUM &&
+              primary_header.crc == secondary_header.crc)
+            {
+              update = tertiary;
+              recovery = secondary;
+              update_header = &tertiary_header;
+              recovery_header = &secondary_header;
+              state->recovery = NXBOOT_SECONDARY_SLOT_NUM;
+              state->update = NXBOOT_TERTIARY_SLOT_NUM;
+            }
+        }
+      else if (primary_header.crc == secondary_header.crc)
         {
-          state->recovery_valid = true;
+          update = tertiary;
+          recovery = secondary;
+          update_header = &tertiary_header;
+          recovery_header = &secondary_header;
+          state->recovery = NXBOOT_SECONDARY_SLOT_NUM;
+          state->update = NXBOOT_TERTIARY_SLOT_NUM;
         }
     }
-  else if (get_image_flag(tertiary, NXBOOT_CONFIRMED_PAGE_INDEX) &&
-           tertiary_header.crc == primary_header.crc &&
-           validate_image(tertiary))
+  else if (IS_INTERNAL_MAGIC(secondary_header.magic))
     {
-      state->recovery_valid = true;
+      update = tertiary;
+      recovery = secondary;
+      update_header = &tertiary_header;
+      recovery_header = &secondary_header;
+      state->recovery = NXBOOT_SECONDARY_SLOT_NUM;
+      state->update = NXBOOT_TERTIARY_SLOT_NUM;
     }
 
-  if (get_image_flag(primary, NXBOOT_CONFIRMED_PAGE_INDEX) ||
-      primary_header.magic == NXBOOT_HEADER_MAGIC_INV)
+  state->recovery_valid = validate_image(recovery);
+  state->recovery_present = primary_header.crc == recovery_header->crc;
+
+  /* The image is confirmed if it has either NXBOOT_HEADER_MAGIC or a
+   * recovery exists.
+   */
+
+  if (primary_header.magic == NXBOOT_HEADER_MAGIC)
     {
       state->primary_confirmed = true;
     }
+  else if (IS_INTERNAL_MAGIC(primary_header.magic))
+    {
+      recovery_pointer = primary_header.magic & NXBOOT_RECOVERY_PTR_MASK;
+      if (recovery_pointer == NXBOOT_SECONDARY_SLOT_NUM)
+        {
+          state->primary_confirmed =
+            primary_header.crc == secondary_header.crc;
+        }
+      else if (recovery_pointer == NXBOOT_TERTIARY_SLOT_NUM)
+        {
+          state->primary_confirmed =
+            primary_header.crc == tertiary_header.crc;
+        }
+    }
 
-  state->next_boot = get_update_type(primary, update, recovery,
-                                     &primary_header, update_header);
+  state->next_boot = get_update_type(state, primary, update, recovery,
+                                     &primary_header, update_header,
+                                     recovery_header);
 
   flash_partition_close(primary);
   flash_partition_close(secondary);
@@ -562,6 +564,33 @@ int nxboot_get_state(struct nxboot_state *state)
   return OK;
 }
 
+/****************************************************************************
+ * Name: nxboot_open_update_partition
+ *
+ * Description:
+ *   Gets the current bootloader state and opens the partition to which an
+ *   update image should be stored. It returns the valid file descriptor to
+ *   this partition, the user is responsible for writing to it and closing
+ *   if afterwards.
+ *
+ * Returned Value:
+ *   Valid file descriptor on success, -1 and sets errno on failure.
+ *
+ ****************************************************************************/
+
+int nxboot_open_update_partition(void)
+{
+  char *path;
+  struct nxboot_state state;
+
+  nxboot_get_state(&state);
+
+  path = state.update == NXBOOT_SECONDARY_SLOT_NUM ?
+    CONFIG_NXBOOT_SECONDARY_SLOT_PATH : CONFIG_NXBOOT_TERTIARY_SLOT_PATH;
+
+  return flash_partition_open(path);
+}
+
 /****************************************************************************
  * Name: nxboot_get_confirm
  *
@@ -579,7 +608,12 @@ int nxboot_get_state(struct nxboot_state *state)
 int nxboot_get_confirm(void)
 {
   int primary;
+  int recovery;
+  int recovery_pointer;
+  char *path;
+  int ret = 0;
   struct nxboot_img_header primary_header;
+  struct nxboot_img_header recovery_header;
 
   primary = flash_partition_open(CONFIG_NXBOOT_PRIMARY_SLOT_PATH);
   if (primary < 0)
@@ -588,13 +622,40 @@ int nxboot_get_confirm(void)
     }
 
   get_image_header(primary, &primary_header);
-  if (get_image_flag(primary, NXBOOT_CONFIRMED_PAGE_INDEX) ||
-      primary_header.magic == NXBOOT_HEADER_MAGIC_INV)
+
+  if (primary_header.magic == NXBOOT_HEADER_MAGIC)
     {
+      close(primary);
       return 1;
     }
+  else if (IS_INTERNAL_MAGIC(primary_header.magic))
+    {
+      recovery_pointer = primary_header.magic & NXBOOT_RECOVERY_PTR_MASK;
+      if (recovery_pointer != 0)
+        {
+          path = recovery_pointer == NXBOOT_SECONDARY_SLOT_NUM ?
+            CONFIG_NXBOOT_SECONDARY_SLOT_PATH :
+            CONFIG_NXBOOT_TERTIARY_SLOT_PATH;
 
-  return 0;
+          recovery = flash_partition_open(path);
+          if (recovery < 0)
+            {
+              close(primary);
+              return ERROR;
+            }
+
+          get_image_header(recovery, &recovery_header);
+          if (primary_header.crc == recovery_header.crc)
+            {
+              ret = 1;
+            }
+
+          close(recovery);
+        }
+    }
+
+  close(primary);
+  return ret;
 }
 
 /****************************************************************************
@@ -614,87 +675,81 @@ int nxboot_confirm(void)
   int ret;
   int update;
   int primary;
-  int secondary;
-  int tertiary;
-  int recovery;
+  int remain;
+  int readsiz;
+  char *path;
+  char *buf;
+  off_t off;
   struct nxboot_state state;
+  struct flash_partition_info info_update;
 
+  ret = OK;
   nxboot_get_state(&state);
   if (state.primary_confirmed)
     {
       return OK;
     }
 
+  path = state.update == NXBOOT_SECONDARY_SLOT_NUM ?
+    CONFIG_NXBOOT_SECONDARY_SLOT_PATH : CONFIG_NXBOOT_TERTIARY_SLOT_PATH;
+
   primary = flash_partition_open(CONFIG_NXBOOT_PRIMARY_SLOT_PATH);
   if (primary < 0)
     {
       return ERROR;
     }
 
-  secondary = flash_partition_open(CONFIG_NXBOOT_SECONDARY_SLOT_PATH);
-  if (secondary < 0)
-    {
-      flash_partition_close(primary);
-      return ERROR;
-    }
-
-  tertiary = flash_partition_open(CONFIG_NXBOOT_TERTIARY_SLOT_PATH);
-  if (tertiary < 0)
+  update = flash_partition_open(path);
+  if (update < 0)
     {
       flash_partition_close(primary);
-      flash_partition_close(secondary);
       return ERROR;
     }
 
-  if (state.update == NXBOOT_SECONDARY_SLOT_NUM)
-    {
-      update = secondary;
-      recovery = tertiary;
-    }
-  else
-    {
-      update = tertiary;
-      recovery = secondary;
-    }
-
-  /* We need to mark both primary and update partitions as confirmed
-   * (update partition will become recovery once confirmed) and
-   * we have to remove confirmed flag from old recovery and set updated
-   * flag there. This is to prevent old recovery still identify as
-   * recovery and not to look as possible update. Therefore remove the
-   * entire last sector (clears confirmed flag) and write updated
-   * flag.
+  /* Confirm the image by creating a recovery. The recovery image is
+   * already present in the update slot, but without the first erase
+   * page. Therefore, just copy the first erase page to the update slot.
    */
 
-  ret = OK;
-  if (set_image_flag(primary, NXBOOT_CONFIRMED_PAGE_INDEX) < 0)
+  if (flash_partition_info(update, &info_update) < 0)
     {
       ret = ERROR;
       goto confirm_done;
     }
 
-  if (set_image_flag(update, NXBOOT_CONFIRMED_PAGE_INDEX) < 0)
-    {
-      ret = ERROR;
-      goto confirm_done;
-    }
+  /* Write by write pages to avoid large array buffering. */
 
-  if (flash_partition_erase_last_sector(recovery) < 0)
-    {
-      ret = ERROR;
-      goto confirm_done;
-    }
+  buf = malloc(info_update.blocksize);
+  remain = info_update.erasesize;
+  off = 0;
 
-  if (set_image_flag(recovery, NXBOOT_UPDATED_PAGE_INDEX) < 0)
+  while (remain > 0)
     {
-      ret = ERROR;
-      goto confirm_done;
+      readsiz = remain > info_update.blocksize ?
+        info_update.blocksize : remain;
+      if (flash_partition_read(primary, buf, readsiz, off) < 0)
+        {
+          free(buf);
+          ret = ERROR;
+          goto confirm_done;
+        }
+
+      if (flash_partition_write(update, buf, readsiz, off) < 0)
+        {
+          free(buf);
+          ret = ERROR;
+          goto confirm_done;
+        }
+
+      off += readsiz;
+      remain -= readsiz;
     }
 
+  free(buf);
+
 confirm_done:
   flash_partition_close(primary);
-  flash_partition_close(secondary);
-  flash_partition_close(tertiary);
+  flash_partition_close(update);
 
   return ret;
 }
@@ -722,7 +777,9 @@ confirm_done:
 int nxboot_perform_update(bool check_only)
 {
   int ret;
+  int primary;
   struct nxboot_state state;
+  struct nxboot_img_header header;
 
   ret = nxboot_get_state(&state);
   if (ret < 0)
@@ -737,10 +794,32 @@ int nxboot_perform_update(bool check_only)
       ret = perform_update(&state, check_only);
       if (ret < 0)
         {
-          syslog(LOG_ERR, "Update process failed. %s\n",
-             strerror(errno));
+          /* Update process failed, raise error and try to boot into
+           * primary.
+           */
+
+          syslog(LOG_ERR, "Update process failed: %s\n", strerror(errno));
         }
     }
 
+  /* Check whether there is a valid image in the primary slot. This just
+   * checks whether the header is valid, but does not calculate the CRC
+   * of the image as this would prolong the boot process.
+   */
+
+  primary = flash_partition_open(CONFIG_NXBOOT_PRIMARY_SLOT_PATH);
+  if (primary < 0)
+    {
+      return ERROR;
+    }
+
+  get_image_header(primary, &header);
+  if (!validate_image_header(&header))
+    {
+      ret = ERROR;
+    }
+
+  flash_partition_close(primary);
+
   return ret;
 }
diff --git a/boot/nxboot/loader/flash.c b/boot/nxboot/loader/flash.c
index 4c32a8831..e7a53b83c 100644
--- a/boot/nxboot/loader/flash.c
+++ b/boot/nxboot/loader/flash.c
@@ -240,10 +240,10 @@ int flash_partition_erase(int fd)
 }
 
 /****************************************************************************
- * Name: flash_partition_erase_last_sector
+ * Name: flash_partition_erase_first_sector
  *
  * Description:
- *   Erases the last sector of the partition
+ *   Erases the first sector of the partition
  *
  * Input parameters:
  *   fd: Valid file descriptor.
@@ -253,18 +253,12 @@ int flash_partition_erase(int fd)
  *
  ****************************************************************************/
 
-int flash_partition_erase_last_sector(int fd)
+int flash_partition_erase_first_sector(int fd)
 {
   int ret;
   struct mtd_erase_s erase;
-  struct flash_partition_info info;
 
-  if (flash_partition_info(fd, &info) < 0)
-    {
-      return ERROR;
-    }
-
-  erase.startblock = info.neraseblocks - 1;
+  erase.startblock = 0;
   erase.nblocks = 1;
 
   ret = ioctl(fd, MTDIOC_ERASESECTORS, &erase);
@@ -308,6 +302,7 @@ int flash_partition_info(int fd, struct 
flash_partition_info *info)
   info->blocksize = geometry.blocksize;
   info->size = geometry.erasesize * geometry.neraseblocks;
   info->neraseblocks = geometry.neraseblocks;
+  info->erasesize = geometry.erasesize;
 
   return OK;
 }
diff --git a/boot/nxboot/loader/flash.h b/boot/nxboot/loader/flash.h
index 653c4c8b9..ef1dee09d 100644
--- a/boot/nxboot/loader/flash.h
+++ b/boot/nxboot/loader/flash.h
@@ -39,6 +39,7 @@ struct flash_partition_info
   int size;
   int blocksize;
   int neraseblocks;
+  int erasesize;
 };
 
 /****************************************************************************
@@ -135,10 +136,10 @@ int flash_partition_read(int fd, void *buf, size_t count, 
off_t off);
 int flash_partition_erase(int fd);
 
 /****************************************************************************
- * Name: flash_partition_erase_last_sector
+ * Name: flash_partition_erase_first_sector
  *
  * Description:
- *   Erases the last sector of the partition
+ *   Erases the first sector of the partition
  *
  * Input parameters:
  *   fd: Valid file descriptor.
@@ -148,7 +149,7 @@ int flash_partition_erase(int fd);
  *
  ****************************************************************************/
 
-int flash_partition_erase_last_sector(int fd);
+int flash_partition_erase_first_sector(int fd);
 
 /****************************************************************************
  * Name: flash_partition_info
diff --git a/boot/nxboot/tools/nximage.py b/boot/nxboot/tools/nximage.py
index 1a9d68a1f..e086c67e5 100644
--- a/boot/nxboot/tools/nximage.py
+++ b/boot/nxboot/tools/nximage.py
@@ -33,58 +33,65 @@ import semantic_version
 
 class NxImage:
     def __init__(
-        self, path: str, result: str, version: str, header_size: int, primary: 
bool
+        self,
+        path: str,
+        result: str,
+        version: str,
+        header_size: int,
+        identifier: int,
     ) -> None:
         self.path = path
         self.result = result
         self.size = os.stat(path).st_size
         self.version = semantic_version.Version(version)
         self.header_size = header_size
-        self.primary = primary
+        self.identifier = identifier
         self.crc = 0
+        self.extd_hdr_ptr = 0
 
-        with open(path, "rb") as f:
-            while data := f.read(io.DEFAULT_BUFFER_SIZE):
-                self.crc = zlib.crc32(data, self.crc)
-
-    def __repr__(self):
+    def __repr__(self) -> str:
         repr = (
             "<NxImage\n"
             f"  path:        {self.path}\n"
             f"  result:      {self.result}\n"
             f"  fsize:       {self.size}\n"
             f"  version:     {self.version}\n"
-            f"  header_size: {self.header_size}\n"
-            f"  primary:     {self.primary}\n"
-            f"  crc:         {self.crc}\n"
+            f"  header_size: {self.header_size:x}\n"
+            f"  identifier:  {self.identifier:x}\n"
+            f"  crc:         {self.crc:x}\n"
             f">"
         )
         return repr
 
-    def add_header(self):
+    def add_header(self) -> None:
         with open(self.path, "r+b") as src, open(self.result, "w+b") as dest:
-            if self.primary:
-                dest.write(b"\xb1\xab\xa0\xac")
-            else:
-                dest.write(b"\x4e\x58\x4f\x53")
+            dest.write(b"\x4e\x58\x4f\x53")
+            dest.write(struct.pack("<H", 0))
+            dest.write(struct.pack("<H", self.header_size))
+            dest.write(struct.pack("<I", 0xFFFFFFFF))
             dest.write(struct.pack("<I", self.size))
-            if self.primary:
-                dest.write(struct.pack("<I", 0xFFFFFFFF))
-            else:
-                dest.write(struct.pack("<I", self.crc))
+            dest.write(struct.pack("<Q", self.identifier))
+            dest.write(struct.pack("<I", self.extd_hdr_ptr))
             dest.write(struct.pack("<H", self.version.major))
             dest.write(struct.pack("<H", self.version.minor))
             dest.write(struct.pack("<H", self.version.patch))
             if not self.version.prerelease:
-                dest.write(struct.pack("@110s", b"\x00"))
+                dest.write(struct.pack("@94s", b"\x00"))
             else:
                 dest.write(
-                    struct.pack("@110s", bytes(self.version.prerelease[0], 
"utf-8"))
+                    struct.pack("@94s", bytes(self.version.prerelease[0], 
"utf-8"))
                 )
             dest.write(bytearray(b"\xff") * (self.header_size - 128))
             while data := src.read(io.DEFAULT_BUFFER_SIZE):
                 dest.write(data)
 
+        with open(self.result, "r+b") as f:
+            f.seek(12)
+            while data := f.read(io.DEFAULT_BUFFER_SIZE):
+                self.crc = zlib.crc32(data, self.crc)
+            f.seek(8)
+            f.write(struct.pack("<I", self.crc))
+
 
 def parse_args() -> argparse.Namespace:
     """Parse passed arguments and return result."""
@@ -101,9 +108,10 @@ def parse_args() -> argparse.Namespace:
         help="Size of the image header.",
     )
     parser.add_argument(
-        "--primary",
-        action="store_true",
-        help="Primary image intended to be uploaded directly to primary 
memory.",
+        "--identifier",
+        type=lambda x: int(x, 0),
+        default=0x0,
+        help="Platform identifier. An image is rejected if its identifier 
doesn't match the one set in bootloader.",
     )
     parser.add_argument(
         "-v",
@@ -126,7 +134,11 @@ def parse_args() -> argparse.Namespace:
 def main() -> None:
     args = parse_args()
     image = NxImage(
-        args.PATH, args.RESULT, args.version, args.header_size, args.primary
+        args.PATH,
+        args.RESULT,
+        args.version,
+        args.header_size,
+        args.identifier,
     )
     image.add_header()
     if args.v:


Reply via email to