zephyr/subsys/dfu/boot/mcuboot.c

329 lines
6.8 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 <flash.h>
#include <zephyr.h>
#include <init.h>
#include <misc/__assert.h>
#include <misc/byteorder.h>
#include <board.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_MAX_ALIGN 8
#define BOOT_MAGIC_SZ 16
#define BOOT_FLAG_SET 0x01
#define BOOT_FLAG_UNSET 0xff
/*
* 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
#define BOOT_MAGIC_UNSET 3
#define BOOT_FLAG_IMAGE_OK 0
#define BOOT_FLAG_COPY_DONE 1
#define FLASH_MIN_WRITE_SIZE FLASH_WRITE_BLOCK_SIZE
#define FLASH_BANK0_OFFSET FLASH_AREA_IMAGE_0_OFFSET
/* FLASH_AREA_IMAGE_XX_YY values used below are auto-generated thanks to DT */
#define FLASH_BANK_SIZE FLASH_AREA_IMAGE_0_SIZE
#define FLASH_BANK1_OFFSET FLASH_AREA_IMAGE_1_OFFSET
#define FLASH_STATE_OFFSET (FLASH_AREA_IMAGE_SCRATCH_OFFSET +\
FLASH_AREA_IMAGE_SCRATCH_SIZE)
#define COPY_DONE_OFFS(bank_offs) (bank_offs + FLASH_BANK_SIZE -\
BOOT_MAGIC_SZ - BOOT_MAX_ALIGN * 2)
#define IMAGE_OK_OFFS(bank_offs) (bank_offs + FLASH_BANK_SIZE - BOOT_MAGIC_SZ -\
BOOT_MAX_ALIGN)
#define MAGIC_OFFS(bank_offs) (bank_offs + FLASH_BANK_SIZE - BOOT_MAGIC_SZ)
const u32_t boot_img_magic[4] = {
0xf395c277,
0x7fefd260,
0x0f505235,
0x8079b62c,
};
static struct device *flash_dev;
static int boot_flag_offs(int flag, u32_t bank_offs, u32_t *offs)
{
switch (flag) {
case BOOT_FLAG_COPY_DONE:
*offs = COPY_DONE_OFFS(bank_offs);
return 0;
case BOOT_FLAG_IMAGE_OK:
*offs = IMAGE_OK_OFFS(bank_offs);
return 0;
default:
return -ENOTSUP;
}
}
static int boot_flash_write(off_t offs, const void *data, size_t len)
{
int rc;
rc = flash_write_protection_set(flash_dev, false);
if (rc) {
return rc;
}
rc = flash_write(flash_dev, offs, data, len);
if (rc) {
return rc;
}
rc = flash_write_protection_set(flash_dev, true);
return rc;
}
static int boot_flag_write(int flag, u32_t bank_offs)
{
u8_t buf[FLASH_MIN_WRITE_SIZE];
u32_t offs;
int rc;
rc = boot_flag_offs(flag, bank_offs, &offs);
if (rc != 0) {
return rc;
}
memset(buf, BOOT_FLAG_UNSET, sizeof(buf));
buf[0] = BOOT_FLAG_SET;
rc = boot_flash_write(offs, buf, sizeof(buf));
return rc;
}
static int boot_flag_read(int flag, u32_t bank_offs)
{
u32_t offs;
int rc;
u8_t flag_val;
rc = boot_flag_offs(flag, bank_offs, &offs);
if (rc != 0) {
return rc;
}
rc = flash_read(flash_dev, offs, &flag_val, sizeof(flag_val));
if (rc != 0) {
return rc;
}
return flag_val;
}
static int boot_image_ok_read(u32_t bank_offs)
{
return boot_flag_read(BOOT_FLAG_IMAGE_OK, bank_offs);
}
static int boot_image_ok_write(u32_t bank_offs)
{
return boot_flag_write(BOOT_FLAG_IMAGE_OK, bank_offs);
}
static int boot_magic_write(u32_t bank_offs)
{
u32_t offs;
int rc;
offs = MAGIC_OFFS(bank_offs);
rc = boot_flash_write(offs, boot_img_magic, BOOT_MAGIC_SZ);
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;
rc = boot_magic_write(FLASH_BANK1_OFFSET);
if (rc == 0 && permanent) {
rc = boot_image_ok_write(FLASH_BANK1_OFFSET);
}
return rc;
}
bool boot_is_img_confirmed(void)
{
return boot_image_ok_read(FLASH_BANK0_OFFSET) == BOOT_FLAG_SET;
}
int boot_write_img_confirmed(void)
{
int rc;
if (boot_image_ok_read(FLASH_BANK0_OFFSET) != BOOT_FLAG_UNSET) {
/* Already confirmed. */
return 0;
}
rc = boot_image_ok_write(FLASH_BANK0_OFFSET);
return rc;
}
int boot_erase_img_bank(u32_t bank_offset)
{
int rc;
rc = flash_write_protection_set(flash_dev, false);
if (rc) {
return rc;
}
rc = flash_erase(flash_dev, bank_offset, FLASH_BANK_SIZE);
if (rc) {
return rc;
}
rc = flash_write_protection_set(flash_dev, true);
return rc;
}
static int boot_init(struct device *dev)
{
ARG_UNUSED(dev);
flash_dev = device_get_binding(FLASH_DEV_NAME);
if (!flash_dev) {
return -ENODEV;
}
return 0;
}
SYS_INIT(boot_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);