zephyr/subsys/dfu/boot/mcuboot.c
Andrzej Puzdrowski 5cfafb041b dfu/boot/mcuboot: made able to compile within MCUBoot
Use rather MCUBoot implementation of some function than
zephyr's implementations once avielabel.

Zephyr implementations are needed while compiling application
supposed to be chainloaded by the MCUBoot or when they are
tested.

Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
2020-11-13 13:37:18 +01:00

741 lines
17 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 <drivers/flash.h>
#include <storage/flash_map.h>
#include <zephyr.h>
#include <init.h>
#include <sys/__assert.h>
#include <sys/byteorder.h>
#include <dfu/mcuboot.h>
/*
* 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
/* Trailer: */
#define BOOT_FLAG_SET 1
#define BOOT_FLAG_BAD 2
#define BOOT_FLAG_UNSET 3
#define BOOT_FLAG_ANY 4 /* NOTE: control only, not dependent on sector */
/*
* 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
*/
#define BOOT_MAGIC_GOOD 1
#define BOOT_MAGIC_BAD 2
#define BOOT_MAGIC_UNSET 3
#define BOOT_MAGIC_ANY 4 /* NOTE: control only, not dependent on sector */
#define BOOT_MAGIC_NOTGOOD 5 /* NOTE: control only, not dependent on sector */
#define BOOT_FLAG_IMAGE_OK 0
#define BOOT_FLAG_COPY_DONE 1
#define FLASH_MIN_WRITE_SIZE \
DT_PROP(DT_CHOSEN(zephyr_flash), write_block_size)
/* FLASH_AREA_ID() values used below are auto-generated by DT */
#ifdef CONFIG_TRUSTED_EXECUTION_NONSECURE
#define FLASH_AREA_IMAGE_PRIMARY FLASH_AREA_ID(image_0_nonsecure)
#define FLASH_AREA_IMAGE_SECONDARY FLASH_AREA_ID(image_1_nonsecure)
#else
#define FLASH_AREA_IMAGE_PRIMARY FLASH_AREA_ID(image_0)
#if FLASH_AREA_LABEL_EXISTS(image_1)
#define FLASH_AREA_IMAGE_SECONDARY FLASH_AREA_ID(image_1)
#endif
#endif /* CONFIG_TRUSTED_EXECUTION_NONSECURE */
#if FLASH_AREA_LABEL_EXISTS(image_scratch)
#define FLASH_AREA_IMAGE_SCRATCH FLASH_AREA_ID(image_scratch)
#endif
#ifdef CONFIG_MCUBOOT_TRAILER_SWAP_TYPE
#define SWAP_TYPE_OFFS(bank_area) ((bank_area)->fa_size -\
BOOT_MAGIC_SZ - BOOT_MAX_ALIGN * 3)
#endif
#define COPY_DONE_OFFS(bank_area) ((bank_area)->fa_size -\
BOOT_MAGIC_SZ - BOOT_MAX_ALIGN * 2)
#define IMAGE_OK_OFFS(bank_area) ((bank_area)->fa_size - BOOT_MAGIC_SZ -\
BOOT_MAX_ALIGN)
#define MAGIC_OFFS(bank_area) ((bank_area)->fa_size - BOOT_MAGIC_SZ)
#if defined(CONFIG_BOOTLOADER_MCUBOOT) || defined(CONFIG_ZTEST)
static const uint32_t boot_img_magic[4] = {
0xf395c277,
0x7fefd260,
0x0f505235,
0x8079b62c,
};
#define BOOT_MAGIC_ARR_SZ ARRAY_SIZE(boot_img_magic)
#endif
struct boot_swap_table {
/** For each field, a value of 0 means "any". */
uint8_t magic_primary_slot;
uint8_t magic_secondary_slot;
uint8_t image_ok_primary_slot;
uint8_t image_ok_secondary_slot;
uint8_t copy_done_primary_slot;
uint8_t swap_type;
};
/** Represents the management state of a single image slot. */
struct boot_swap_state {
uint8_t magic; /* One of the BOOT_MAGIC_[...] values. */
uint8_t swap_type; /* One of the BOOT_SWAP_TYPE_[...] values. */
uint8_t copy_done; /* One of the BOOT_FLAG_[...] values. */
uint8_t image_ok; /* One of the BOOT_FLAG_[...] values. */
};
#ifdef FLASH_AREA_IMAGE_SECONDARY
/**
* This set of tables maps image trailer contents to swap operation type.
* When searching for a match, these tables must be iterated sequentially.
*
* NOTE: the table order is very important. The settings in the secondary
* slot always are priority to the primary slot and should be located
* earlier in the table.
*
* The table lists only states where there is action needs to be taken by
* the bootloader, as in starting/finishing a swap operation.
*/
static const struct boot_swap_table boot_swap_tables[] = {
{
/* | slot-0 | slot-1 |
*----------+------------+------------|
* magic | Any | Good |
* image-ok | Any | Unset |
* ---------+------------+------------+
* swap: test |
* -----------------------------------'
*/
.magic_primary_slot = BOOT_MAGIC_ANY,
.magic_secondary_slot = BOOT_MAGIC_GOOD,
.image_ok_primary_slot = BOOT_FLAG_ANY,
.image_ok_secondary_slot = BOOT_FLAG_UNSET,
.copy_done_primary_slot = BOOT_FLAG_ANY,
.swap_type = BOOT_SWAP_TYPE_TEST,
},
{
/* | slot-0 | slot-1 |
*----------+------------+------------|
* magic | Any | Good |
* image-ok | Any | 0x01 |
* ---------+------------+------------+
* swap: permanent |
* -----------------------------------'
*/
.magic_primary_slot = BOOT_MAGIC_ANY,
.magic_secondary_slot = BOOT_MAGIC_GOOD,
.image_ok_primary_slot = BOOT_FLAG_ANY,
.image_ok_secondary_slot = BOOT_FLAG_SET,
.copy_done_primary_slot = BOOT_FLAG_ANY,
.swap_type = BOOT_SWAP_TYPE_PERM,
},
{
/* | slot-0 | slot-1 |
*----------+------------+------------|
* magic | Good | Unset |
* image-ok | Unset | Any |
* ---------+------------+------------+
* swap: revert (test image running) |
* -----------------------------------'
*/
.magic_primary_slot = BOOT_MAGIC_GOOD,
.magic_secondary_slot = BOOT_MAGIC_UNSET,
.image_ok_primary_slot = BOOT_FLAG_UNSET,
.image_ok_secondary_slot = BOOT_FLAG_ANY,
.copy_done_primary_slot = BOOT_FLAG_SET,
.swap_type = BOOT_SWAP_TYPE_REVERT,
},
};
#define BOOT_SWAP_TABLES_COUNT (ARRAY_SIZE(boot_swap_tables))
#endif
#if defined(CONFIG_BOOTLOADER_MCUBOOT) || defined(CONFIG_ZTEST)
static int boot_magic_decode(const uint32_t *magic)
{
if (memcmp(magic, boot_img_magic, BOOT_MAGIC_SZ) == 0) {
return BOOT_MAGIC_GOOD;
}
return BOOT_MAGIC_BAD;
}
static int boot_flag_decode(uint8_t flag)
{
if (flag != BOOT_FLAG_SET) {
return BOOT_FLAG_BAD;
}
return BOOT_FLAG_SET;
}
#endif
#if defined(CONFIG_BOOTLOADER_MCUBOOT) || defined(CONFIG_ZTEST)
int flash_area_read_is_empty(const struct flash_area *fa, uint32_t off,
void *dst, uint32_t len)
{
const uint8_t erase_val = flash_area_erased_val(fa);
uint8_t *u8dst;
uint8_t i;
int rc;
rc = flash_area_read(fa, off, dst, len);
if (rc) {
return rc;
}
for (i = 0, u8dst = (uint8_t *)dst; i < len; i++) {
if (u8dst[i] != erase_val) {
return 0;
}
}
return 1;
}
#endif
static int erased_flag_val(uint8_t bank_id)
{
const struct flash_area *fa;
int rc;
rc = flash_area_open(bank_id, &fa);
if (rc) {
return -EINVAL;
}
return flash_area_erased_val(fa);
}
#if !defined(CONFIG_BOOTLOADER_MCUBOOT) && !defined(CONFIG_ZTEST)
/* Provided by MCUBoot */
int boot_magic_compatible_check(uint8_t tbl_val, uint8_t val);
#else
/**
* Determines if a status source table is satisfied by the specified magic
* code.
*
* @param tbl_val A magic field from a status source table.
* @param val The magic value in a trailer, encoded as a
* BOOT_MAGIC_[...].
*
* @return 1 if the two values are compatible;
* 0 otherwise.
*/
int boot_magic_compatible_check(uint8_t tbl_val, uint8_t val)
{
switch (tbl_val) {
case BOOT_MAGIC_ANY:
return 1;
case BOOT_MAGIC_NOTGOOD:
return val != BOOT_MAGIC_GOOD;
default:
return tbl_val == val;
}
}
#endif
static int boot_flag_offs(int flag, const struct flash_area *fa, uint32_t *offs)
{
switch (flag) {
case BOOT_FLAG_COPY_DONE:
*offs = COPY_DONE_OFFS(fa);
return 0;
case BOOT_FLAG_IMAGE_OK:
*offs = IMAGE_OK_OFFS(fa);
return 0;
default:
return -ENOTSUP;
}
}
static int boot_write_trailer_byte(const struct flash_area *fa, uint32_t off,
uint8_t val)
{
uint8_t buf[BOOT_MAX_ALIGN];
uint8_t align;
uint8_t erased_val;
int rc;
align = flash_area_align(fa);
__ASSERT_NO_MSG(align <= BOOT_MAX_ALIGN);
erased_val = flash_area_erased_val(fa);
memset(buf, erased_val, BOOT_MAX_ALIGN);
buf[0] = val;
rc = flash_area_write(fa, off, buf, align);
if (rc != 0) {
return -EIO;
}
return 0;
}
static int boot_flag_write(int flag, uint8_t bank_id)
{
const struct flash_area *fa;
uint32_t offs;
int rc;
rc = flash_area_open(bank_id, &fa);
if (rc) {
return rc;
}
rc = boot_flag_offs(flag, fa, &offs);
if (rc != 0) {
flash_area_close(fa);
return rc;
}
rc = boot_write_trailer_byte(fa, offs, BOOT_FLAG_SET);
flash_area_close(fa);
return rc;
}
static int boot_flag_read(int flag, uint8_t bank_id)
{
const struct flash_area *fa;
uint32_t offs;
int rc;
uint8_t flag_val;
rc = flash_area_open(bank_id, &fa);
if (rc) {
return rc;
}
rc = boot_flag_offs(flag, fa, &offs);
if (rc != 0) {
flash_area_close(fa);
return rc;
}
rc = flash_area_read(fa, offs, &flag_val, sizeof(flag_val));
if (rc != 0) {
return rc;
}
return flag_val;
}
static int boot_image_ok_read(uint8_t bank_id)
{
return boot_flag_read(BOOT_FLAG_IMAGE_OK, bank_id);
}
static int boot_image_ok_write(uint8_t bank_id)
{
return boot_flag_write(BOOT_FLAG_IMAGE_OK, bank_id);
}
#ifdef FLASH_AREA_IMAGE_SECONDARY
static int boot_magic_write(uint8_t bank_id)
{
const struct flash_area *fa;
uint32_t offs;
int rc;
rc = flash_area_open(bank_id, &fa);
if (rc) {
return rc;
}
offs = MAGIC_OFFS(fa);
rc = flash_area_write(fa, offs, boot_img_magic, BOOT_MAGIC_SZ);
flash_area_close(fa);
return rc;
}
#ifdef CONFIG_MCUBOOT_TRAILER_SWAP_TYPE
static int boot_swap_type_write(uint8_t bank_id, uint8_t swap_type)
{
const struct flash_area *fa;
uint32_t offs;
int rc;
rc = flash_area_open(bank_id, &fa);
if (rc) {
return rc;
}
offs = SWAP_TYPE_OFFS(fa);
rc = boot_write_trailer_byte(fa, offs, swap_type);
flash_area_close(fa);
return rc;
}
#endif /* CONFIG_MCUBOOT_TRAILER_SWAP_TYPE */
#endif /* FLASH_AREA_IMAGE_SECONDARY */
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;
}
#if defined(CONFIG_BOOTLOADER_MCUBOOT) || defined(CONFIG_ZTEST)
static int boot_read_swap_state(const struct flash_area *fa,
struct boot_swap_state *state)
{
uint32_t magic[BOOT_MAGIC_ARR_SZ];
uint32_t off;
int rc;
off = MAGIC_OFFS(fa);
rc = flash_area_read_is_empty(fa, off, magic, BOOT_MAGIC_SZ);
if (rc < 0) {
return -EIO;
}
if (rc == 1) {
state->magic = BOOT_MAGIC_UNSET;
} else {
state->magic = boot_magic_decode(magic);
}
#ifdef CONFIG_MCUBOOT_TRAILER_SWAP_TYPE
off = SWAP_TYPE_OFFS(fa);
rc = flash_area_read_is_empty(fa, off, &state->swap_type,
sizeof(state->swap_type));
if (rc < 0) {
return -EIO;
}
if (rc == 1 || state->swap_type > BOOT_SWAP_TYPE_REVERT) {
state->swap_type = BOOT_SWAP_TYPE_NONE;
}
off = COPY_DONE_OFFS(fa);
rc = flash_area_read_is_empty(fa, off, &state->copy_done,
sizeof(state->copy_done));
if (rc < 0) {
return -EIO;
}
if (rc == 1) {
state->copy_done = BOOT_FLAG_UNSET;
} else {
state->copy_done = boot_flag_decode(state->copy_done);
}
#else
#ifdef FLASH_AREA_IMAGE_SCRATCH
if (fa->fa_id != FLASH_AREA_IMAGE_SCRATCH) {
#endif
off = COPY_DONE_OFFS(fa);
rc = flash_area_read_is_empty(fa, off, &state->copy_done,
sizeof(state->copy_done));
if (rc < 0) {
return -EIO;
}
if (rc == 1) {
state->copy_done = BOOT_FLAG_UNSET;
} else {
state->copy_done = boot_flag_decode(state->copy_done);
}
#ifdef FLASH_AREA_IMAGE_SCRATCH
}
#endif
#endif
off = IMAGE_OK_OFFS(fa);
rc = flash_area_read_is_empty(fa, off, &state->image_ok,
sizeof(state->image_ok));
if (rc < 0) {
return -EIO;
}
if (rc == 1) {
state->image_ok = BOOT_FLAG_UNSET;
} else {
state->image_ok = boot_flag_decode(state->image_ok);
}
return 0;
}
#endif
#if !defined(CONFIG_BOOTLOADER_MCUBOOT) && !defined(CONFIG_ZTEST)
/* provided by MCUBoot */
int
boot_read_swap_state_by_id(int flash_area_id, struct boot_swap_state *state);
#else
/**
* Reads the image trailer from the scratch area.
*/
int
boot_read_swap_state_by_id(int flash_area_id, struct boot_swap_state *state)
{
const struct flash_area *fap;
int rc;
switch (flash_area_id) {
#ifdef FLASH_AREA_IMAGE_SCRATCH
case FLASH_AREA_IMAGE_SCRATCH:
#endif
case FLASH_AREA_IMAGE_PRIMARY:
#ifdef FLASH_AREA_IMAGE_SECONDARY
case FLASH_AREA_IMAGE_SECONDARY:
#endif
rc = flash_area_open(flash_area_id, &fap);
if (rc != 0) {
return -EIO;
}
break;
default:
return -EINVAL;
}
rc = boot_read_swap_state(fap, state);
flash_area_close(fap);
return rc;
}
#endif
/* equivalent of boot_swap_type() in mcuboot bootutil_misc.c */
int mcuboot_swap_type(void)
{
#ifdef FLASH_AREA_IMAGE_SECONDARY
const struct boot_swap_table *table;
struct boot_swap_state primary_slot;
struct boot_swap_state secondary_slot;
int rc;
size_t i;
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY,
&primary_slot);
if (rc) {
return rc;
}
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY,
&secondary_slot);
if (rc) {
return rc;
}
for (i = 0; i < BOOT_SWAP_TABLES_COUNT; i++) {
table = boot_swap_tables + i;
if (boot_magic_compatible_check(table->magic_primary_slot,
primary_slot.magic)
&&
boot_magic_compatible_check(table->magic_secondary_slot,
secondary_slot.magic)
&&
(table->image_ok_primary_slot == BOOT_FLAG_ANY ||
table->image_ok_primary_slot == primary_slot.image_ok)
&&
(table->image_ok_secondary_slot == BOOT_FLAG_ANY ||
table->image_ok_secondary_slot == secondary_slot.image_ok)
&&
(table->copy_done_primary_slot == BOOT_FLAG_ANY ||
table->copy_done_primary_slot == primary_slot.copy_done)) {
__ASSERT_NO_MSG(table->swap_type == BOOT_SWAP_TYPE_TEST ||
table->swap_type == BOOT_SWAP_TYPE_PERM ||
table->swap_type == BOOT_SWAP_TYPE_REVERT);
return table->swap_type;
}
}
#endif
return BOOT_SWAP_TYPE_NONE;
}
int boot_request_upgrade(int permanent)
{
#ifdef FLASH_AREA_IMAGE_SECONDARY
#ifdef CONFIG_MCUBOOT_TRAILER_SWAP_TYPE
uint8_t swap_type;
#endif
int rc;
rc = boot_magic_write(FLASH_AREA_IMAGE_SECONDARY);
if (rc) {
goto op_end;
}
if (permanent) {
rc = boot_image_ok_write(FLASH_AREA_IMAGE_SECONDARY);
#ifdef CONFIG_MCUBOOT_TRAILER_SWAP_TYPE
if (rc) {
goto op_end;
}
swap_type = BOOT_SWAP_TYPE_PERM;
} else {
swap_type = BOOT_SWAP_TYPE_TEST;
}
rc = boot_swap_type_write(FLASH_AREA_IMAGE_SECONDARY, swap_type);
#else
}
#endif
op_end:
return rc;
#else
return 0;
#endif /* FLASH_AREA_IMAGE_SECONDARY */
}
bool boot_is_img_confirmed(void)
{
return boot_image_ok_read(FLASH_AREA_IMAGE_PRIMARY) == BOOT_FLAG_SET;
}
int boot_write_img_confirmed(void)
{
int rc;
if (boot_image_ok_read(FLASH_AREA_IMAGE_PRIMARY) !=
erased_flag_val(FLASH_AREA_IMAGE_PRIMARY)) {
/* Already confirmed. */
return 0;
}
rc = boot_image_ok_write(FLASH_AREA_IMAGE_PRIMARY);
return rc;
}
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_erase(fa, 0, fa->fa_size);
flash_area_close(fa);
return rc;
}