/* * Copyright (c) 2017 Nordic Semiconductor ASA * Copyright (c) 2016-2017 Linaro Limited * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include "bootutil/bootutil_public.h" #include #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD) #include #include #endif #include "mcuboot_priv.h" LOG_MODULE_REGISTER(mcuboot_dfu, LOG_LEVEL_DBG); /* * Helpers for image headers and trailers, as defined by mcuboot. */ /* * 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 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD) /* For RAM LOAD mode, the active image must be fetched from the bootloader */ static uint8_t boot_fetch_active_slot(void); #define ACTIVE_SLOT_FLASH_AREA_ID boot_fetch_active_slot() #define INVALID_SLOT_ID 255 #else /* Get active partition. zephyr,code-partition chosen node must be defined */ #define ACTIVE_SLOT_FLASH_AREA_ID DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_code_partition)) #endif /* * Raw (on-flash) representation of the v1 image header. */ struct mcuboot_v1_raw_header { uint32_t header_magic; uint32_t image_load_address; uint16_t header_size; uint16_t pad; uint32_t image_size; uint32_t image_flags; struct { uint8_t major; uint8_t minor; uint16_t revision; uint32_t build_num; } version; uint32_t pad2; } __packed; /* * End of strict defines */ #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD) static uint8_t boot_fetch_active_slot(void) { int rc; uint8_t slot; rc = blinfo_lookup(BLINFO_RUNNING_SLOT, &slot, sizeof(slot)); if (rc <= 0) { LOG_ERR("Failed to fetch active slot: %d", rc); return INVALID_SLOT_ID; } LOG_DBG("Active slot: %d", slot); return slot; } #endif static int boot_read_v1_header(uint8_t area_id, struct mcuboot_v1_raw_header *v1_raw) { const struct flash_area *fa; int rc; rc = flash_area_open(area_id, &fa); if (rc) { return rc; } /* * Read and sanity-check the raw header. */ rc = flash_area_read(fa, 0, v1_raw, sizeof(*v1_raw)); flash_area_close(fa); 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_ROM_START_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(uint8_t area_id, 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(uint32_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(area_id, &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 = 1U; 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 mcuboot_swap_type_multi(int image_index) { return boot_swap_type_multi(image_index); } int mcuboot_swap_type(void) { #ifdef FLASH_AREA_IMAGE_SECONDARY return boot_swap_type(); #else return BOOT_SWAP_TYPE_NONE; #endif } int boot_request_upgrade(int permanent) { #ifdef FLASH_AREA_IMAGE_SECONDARY int rc; rc = boot_set_pending(permanent); if (rc) { return -EFAULT; } #endif /* FLASH_AREA_IMAGE_SECONDARY */ return 0; } int boot_request_upgrade_multi(int image_index, int permanent) { int rc; rc = boot_set_pending_multi(image_index, permanent); if (rc) { return -EFAULT; } return 0; } bool boot_is_img_confirmed(void) { struct boot_swap_state state; const struct flash_area *fa; int rc; rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY, &fa); if (rc) { return false; } rc = boot_read_swap_state(fa, &state); if (rc != 0) { return false; } if (state.magic == BOOT_MAGIC_UNSET) { /* This is initial/preprogramed image. * Such image can neither be reverted nor physically confirmed. * Treat this image as confirmed which ensures consistency * with `boot_write_img_confirmed...()` procedures. */ return true; } return state.image_ok == BOOT_FLAG_SET; } int boot_write_img_confirmed(void) { const struct flash_area *fa; int rc = 0; if (flash_area_open(ACTIVE_SLOT_FLASH_AREA_ID, &fa) != 0) { return -EIO; } rc = boot_set_next(fa, true, true); flash_area_close(fa); return rc; } int boot_write_img_confirmed_multi(int image_index) { int rc; rc = boot_set_confirmed_multi(image_index); if (rc) { return -EIO; } return 0; } int boot_erase_img_bank(uint8_t area_id) { const struct flash_area *fa; int rc; rc = flash_area_open(area_id, &fa); if (rc) { return rc; } rc = flash_area_flatten(fa, 0, fa->fa_size); flash_area_close(fa); return rc; } ssize_t boot_get_trailer_status_offset(size_t area_size) { return (ssize_t)area_size - BOOT_MAGIC_SZ - BOOT_MAX_ALIGN * 2; } ssize_t boot_get_area_trailer_status_offset(uint8_t area_id) { int rc; const struct flash_area *fa; ssize_t offset; rc = flash_area_open(area_id, &fa); if (rc) { return rc; } offset = boot_get_trailer_status_offset(fa->fa_size); flash_area_close(fa); if (offset < 0) { return -EFAULT; } return offset; }