diff --git a/include/dfu/mcuboot.h b/include/dfu/mcuboot.h index e8a8641c389..301d7d9e092 100644 --- a/include/dfu/mcuboot.h +++ b/include/dfu/mcuboot.h @@ -9,6 +9,88 @@ #define __MCUBOOT_H__ #include +#include + +#include + +/** + * @brief MCUboot image header representation for image version + * + * The header for an MCUboot firmware image contains an embedded + * version number, in semantic versioning format. This structure + * represents the information it contains. + */ +struct mcuboot_img_sem_ver { + u8_t major; + u8_t minor; + u16_t revision; + u32_t build_num; +}; + +/** + * @brief Model for the MCUboot image header as of version 1 + * + * This represents the data present in the image header, in version 1 + * of the header format. + * + * Some information present in the header but not currently relevant + * to applications is omitted. + */ +struct mcuboot_img_header_v1 { + /** The size of the image, in bytes. */ + u32_t image_size; + /** The image version. */ + struct mcuboot_img_sem_ver sem_ver; +}; + +/** + * @brief Model for the MCUBoot image header + * + * This contains the decoded image header, along with the major + * version of MCUboot that the header was built for. + * + * (The MCUboot project guarantees that incompatible changes to the + * image header will result in major version changes to the bootloader + * itself, and will be detectable in the persistent representation of + * the header.) + */ +struct mcuboot_img_header { + /** + * The version of MCUboot the header is built for. + * + * The value 1 corresponds to MCUboot versions 1.x.y. + */ + u32_t mcuboot_version; + /** + * The header information. It is only valid to access fields + * in the union member corresponding to the mcuboot_version + * field above. + */ + union { + /** Header information for MCUboot version 1. */ + struct mcuboot_img_header_v1 v1; + } h; +}; + +/** + * @brief Read the MCUboot image header information from an image bank. + * + * This attempts to parse the image header, which must begin at offset + * @a bank_offset from the beginning of the flash device used by + * MCUboot. + * + * @param bank_offset Offset of the image header from the start of the + * flash device used by MCUboot to store firmware. + * @param header On success, the returned header information is available + * in this structure. + * @param header_size Size of the header structure passed by the caller. + * If this is not large enough to contain all of the + * necessary information, an error is returned. + * @return Zero on success, a negative value on error. + */ +int boot_read_bank_header(u32_t bank_offset, + struct mcuboot_img_header *header, + size_t header_size); /** * @brief Check if the currently running image is confirmed as OK. diff --git a/subsys/dfu/boot/mcuboot.c b/subsys/dfu/boot/mcuboot.c index c418ac2217b..93a7190d66d 100644 --- a/subsys/dfu/boot/mcuboot.c +++ b/subsys/dfu/boot/mcuboot.c @@ -13,22 +13,51 @@ #include #include +#include #include #include /* - * Helpers for image trailer, as defined by mcuboot. + * Helpers for image headers and trailers, as defined by mcuboot. */ /* - * Strict defines: Defines in block below must be equal to coresponding - * mcuboot defines + * Strict defines: the definitions in the following block contain + * values which are MCUboot implementation requirements. */ + +/* Header: */ +#define BOOT_HEADER_MAGIC_V1 0x96f3b83d +#define BOOT_HEADER_SIZE_V1 32 + +/* Trailer: */ #define BOOT_MAX_ALIGN 8 #define BOOT_MAGIC_SZ 16 #define BOOT_FLAG_SET 0x01 #define BOOT_FLAG_UNSET 0xff -/* end_of Strict defines */ + +/* + * Raw (on-flash) representation of the v1 image header. + */ +struct mcuboot_v1_raw_header { + u32_t header_magic; + u32_t image_load_address; + u16_t header_size; + u16_t pad; + u32_t image_size; + u32_t image_flags; + struct { + u8_t major; + u8_t minor; + u16_t revision; + u32_t build_num; + } version; + u32_t pad2; +} __packed; + +/* + * End of strict defines + */ #define BOOT_MAGIC_GOOD 1 #define BOOT_MAGIC_BAD 2 @@ -156,6 +185,86 @@ static int boot_magic_write(u32_t bank_offs) return rc; } +static int boot_read_v1_header(u32_t bank_offset, + struct mcuboot_v1_raw_header *v1_raw) +{ + int rc; + + /* + * Read and sanity-check the raw header. + */ + rc = flash_read(flash_dev, bank_offset, v1_raw, sizeof(*v1_raw)); + if (rc) { + return rc; + } + + v1_raw->header_magic = sys_le32_to_cpu(v1_raw->header_magic); + v1_raw->image_load_address = + sys_le32_to_cpu(v1_raw->image_load_address); + v1_raw->header_size = sys_le16_to_cpu(v1_raw->header_size); + v1_raw->image_size = sys_le32_to_cpu(v1_raw->image_size); + v1_raw->image_flags = sys_le32_to_cpu(v1_raw->image_flags); + v1_raw->version.revision = + sys_le16_to_cpu(v1_raw->version.revision); + v1_raw->version.build_num = + sys_le32_to_cpu(v1_raw->version.build_num); + + /* + * Sanity checks. + * + * Larger values in header_size than BOOT_HEADER_SIZE_V1 are + * possible, e.g. if Zephyr was linked with + * CONFIG_TEXT_SECTION_OFFSET > BOOT_HEADER_SIZE_V1. + */ + if ((v1_raw->header_magic != BOOT_HEADER_MAGIC_V1) || + (v1_raw->header_size < BOOT_HEADER_SIZE_V1)) { + return -EIO; + } + + return 0; +} + +int boot_read_bank_header(u32_t bank_offset, + struct mcuboot_img_header *header, + size_t header_size) +{ + int rc; + struct mcuboot_v1_raw_header v1_raw; + struct mcuboot_img_sem_ver *sem_ver; + size_t v1_min_size = (sizeof(u32_t) + + sizeof(struct mcuboot_img_header_v1)); + + /* + * Only version 1 image headers are supported. + */ + if (header_size < v1_min_size) { + return -ENOMEM; + } + rc = boot_read_v1_header(bank_offset, &v1_raw); + if (rc) { + return rc; + } + + /* + * Copy just the fields we care about into the return parameter. + * + * - header_magic: skip (only used to check format) + * - image_load_address: skip (only matters for PIC code) + * - header_size: skip (only used to check format) + * - image_size: include + * - image_flags: skip (all unsupported or not relevant) + * - version: include + */ + header->mcuboot_version = 1; + header->h.v1.image_size = v1_raw.image_size; + sem_ver = &header->h.v1.sem_ver; + sem_ver->major = v1_raw.version.major; + sem_ver->minor = v1_raw.version.minor; + sem_ver->revision = v1_raw.version.revision; + sem_ver->build_num = v1_raw.version.build_num; + return 0; +} + int boot_request_upgrade(int permanent) { int rc;