zephyr/subsys/dfu/boot/mcuboot.c

323 lines
6.6 KiB
C

/*
* Copyright (c) 2017 Nordic Semiconductor ASA
* Copyright (c) 2016-2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/storage/flash_map.h>
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h>
#include "bootutil/bootutil_public.h"
#include <zephyr/dfu/mcuboot.h>
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
#include <bootutil/boot_status.h>
#include <zephyr/retention/blinfo.h>
#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;
}