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_PRERELEASE_MAXLEN 110 +#define NXBOOT_HEADER_MAGIC_INT_MASK 0xfffffff0 +#define NXBOOT_RECOVERY_PTR_MASK 0x3 + +#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) + /* 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. + */ + + magic = IS_INTERNAL_MAGIC(header.magic) ? + NXBOOT_HEADER_MAGIC : NXBOOT_HEADER_MAGIC_INT; + + if (update) { + /* This is update operation, add pointer to recovery image to the + * image's magic. + */ + + magic |= state->update; + } + + if (flash_partition_read(from, buf, blocksize, 0) < 0) + { + free(buf); return ERROR; } - remain = header.size + CONFIG_NXBOOT_HEADER_SIZE; - off = 0; - if (header.magic == NXBOOT_HEADER_MAGIC_INV) + memcpy(buf + offsetof(struct nxboot_img_header, magic), &magic, + sizeof magic); + if (flash_partition_write(where, buf, blocksize, 0) < 0) { - /* 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. - */ - - magic = NXBOOT_HEADER_MAGIC; - crc = calculate_crc(from, &header); - if (flash_partition_read(from, buf, blocksize, 0) < 0) - { - free(buf); - return ERROR; - } - - 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; - } - - off += blocksize; - remain -= blocksize; + 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)) + bool primary_valid = validate_image(primary); + + if (update_header->magic == NXBOOT_HEADER_MAGIC && validate_image(update)) { - return NXBOOT_UPDATE_TYPE_REVERT; + if (primary_header->crc != update_header->crc || + !compare_versions(&primary_header->img_version, + &update_header->img_version) || !primary_valid) + { + return NXBOOT_UPDATE_TYPE_UPDATE; + } } - if (!get_image_flag(update, NXBOOT_CONFIRMED_PAGE_INDEX) && - !get_image_flag(update, NXBOOT_UPDATED_PAGE_INDEX) && - validate_image(update)) + if (IS_INTERNAL_MAGIC(recovery_header->magic) && state->recovery_valid && + ((IS_INTERNAL_MAGIC(primary_header->magic) && + !state->primary_confirmed) || !primary_valid)) { - if (compare_versions(&primary_header->img_version, - &update_header->img_version) && - validate_image(primary)) - { - return NXBOOT_UPDATE_TYPE_NONE; - } - - return NXBOOT_UPDATE_TYPE_UPDATE; + 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); + 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. + */ - /* Mark update slot as updated. This is to prevent repeated - * updates. - */ - - 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)) { - state->recovery_valid = true; + 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) + { + 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) + update = flash_partition_open(path); + if (update < 0) { flash_partition_close(primary); return ERROR; } - tertiary = flash_partition_open(CONFIG_NXBOOT_TERTIARY_SLOT_PATH); - if (tertiary < 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) + /* Write by write pages to avoid large array buffering. */ + + buf = malloc(info_update.blocksize); + remain = info_update.erasesize; + off = 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; } - if (flash_partition_erase_last_sector(recovery) < 0) - { - ret = ERROR; - goto confirm_done; - } - - if (set_image_flag(recovery, NXBOOT_UPDATED_PAGE_INDEX) < 0) - { - ret = ERROR; - goto confirm_done; - } + 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 = ( "" ) 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(" 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: