mirror of https://github.com/wolfSSL/wolfBoot.git
286 lines
9.7 KiB
C
286 lines
9.7 KiB
C
/* update_flash.c
|
|
*
|
|
* Implementation for Flash based updater
|
|
*
|
|
*
|
|
* Copyright (C) 2020 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfBoot.
|
|
*
|
|
* wolfBoot is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfBoot is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include "loader.h"
|
|
#include "image.h"
|
|
#include "hal.h"
|
|
#include "spi_flash.h"
|
|
#include "wolfboot/wolfboot.h"
|
|
|
|
|
|
#ifdef RAM_CODE
|
|
extern unsigned int _start_text;
|
|
static volatile const uint32_t __attribute__((used)) wolfboot_version = WOLFBOOT_VERSION;
|
|
|
|
#ifndef BUFFER_DECLARED
|
|
#define BUFFER_DECLARED
|
|
static uint8_t buffer[FLASHBUFFER_SIZE];
|
|
#endif
|
|
|
|
static void RAMFUNCTION wolfBoot_erase_bootloader(void)
|
|
{
|
|
uint32_t *start = (uint32_t *)&_start_text;
|
|
uint32_t len = WOLFBOOT_PARTITION_BOOT_ADDRESS - (uint32_t)start;
|
|
hal_flash_erase((uint32_t)start, len);
|
|
|
|
}
|
|
|
|
#include <string.h>
|
|
|
|
static void RAMFUNCTION wolfBoot_self_update(struct wolfBoot_image *src)
|
|
{
|
|
uint32_t pos = 0;
|
|
uint32_t src_offset = IMAGE_HEADER_SIZE;
|
|
|
|
hal_flash_unlock();
|
|
wolfBoot_erase_bootloader();
|
|
#ifdef EXT_FLASH
|
|
if (PART_IS_EXT(src)) {
|
|
while (pos < src->fw_size) {
|
|
uint8_t buffer[FLASHBUFFER_SIZE];
|
|
if (src_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) {
|
|
ext_flash_check_read((uintptr_t)(src->hdr) + src_offset + pos, (void *)buffer, FLASHBUFFER_SIZE);
|
|
hal_flash_write(pos + (uint32_t)&_start_text, buffer, FLASHBUFFER_SIZE);
|
|
}
|
|
pos += FLASHBUFFER_SIZE;
|
|
}
|
|
goto lock_and_reset;
|
|
}
|
|
#endif
|
|
while (pos < src->fw_size) {
|
|
if (src_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) {
|
|
uint8_t *orig = (uint8_t*)(src->hdr + src_offset + pos);
|
|
hal_flash_write(pos + (uint32_t)&_start_text, orig, FLASHBUFFER_SIZE);
|
|
}
|
|
pos += FLASHBUFFER_SIZE;
|
|
}
|
|
|
|
lock_and_reset:
|
|
hal_flash_lock();
|
|
arch_reboot();
|
|
}
|
|
|
|
void wolfBoot_check_self_update(void)
|
|
{
|
|
uint8_t st;
|
|
struct wolfBoot_image update;
|
|
uint8_t *update_type;
|
|
uint32_t update_version;
|
|
|
|
/* Check for self update in the UPDATE partition */
|
|
if ((wolfBoot_get_partition_state(PART_UPDATE, &st) == 0) && (st == IMG_STATE_UPDATING) &&
|
|
(wolfBoot_open_image(&update, PART_UPDATE) == 0) &&
|
|
wolfBoot_get_image_type(PART_UPDATE) == (HDR_IMG_TYPE_WOLFBOOT | HDR_IMG_TYPE_AUTH)) {
|
|
uint32_t update_version = wolfBoot_update_firmware_version();
|
|
if (update_version <= wolfboot_version) {
|
|
hal_flash_unlock();
|
|
wolfBoot_erase_partition(PART_UPDATE);
|
|
hal_flash_lock();
|
|
return;
|
|
}
|
|
if (wolfBoot_verify_integrity(&update) < 0)
|
|
return;
|
|
if (wolfBoot_verify_authenticity(&update) < 0)
|
|
return;
|
|
wolfBoot_self_update(&update);
|
|
}
|
|
}
|
|
#endif /* RAM_CODE for self_update */
|
|
|
|
static int wolfBoot_copy_sector(struct wolfBoot_image *src, struct wolfBoot_image *dst, uint32_t sector)
|
|
{
|
|
uint32_t pos = 0;
|
|
uint32_t src_sector_offset = (sector * WOLFBOOT_SECTOR_SIZE);
|
|
uint32_t dst_sector_offset = (sector * WOLFBOOT_SECTOR_SIZE);
|
|
if (src == dst)
|
|
return 0;
|
|
|
|
if (src->part == PART_SWAP)
|
|
src_sector_offset = 0;
|
|
if (dst->part == PART_SWAP)
|
|
dst_sector_offset = 0;
|
|
#ifdef EXT_FLASH
|
|
if (PART_IS_EXT(src)) {
|
|
#ifndef BUFFER_DECLARED
|
|
#define BUFFER_DECLARED
|
|
static uint8_t buffer[FLASHBUFFER_SIZE];
|
|
#endif
|
|
wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE);
|
|
while (pos < WOLFBOOT_SECTOR_SIZE) {
|
|
if (src_sector_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) {
|
|
ext_flash_check_read((uintptr_t)(src->hdr) + src_sector_offset + pos, (void *)buffer, FLASHBUFFER_SIZE);
|
|
wb_flash_write(dst, dst_sector_offset + pos, buffer, FLASHBUFFER_SIZE);
|
|
}
|
|
pos += FLASHBUFFER_SIZE;
|
|
}
|
|
return pos;
|
|
}
|
|
#endif
|
|
wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE);
|
|
while (pos < WOLFBOOT_SECTOR_SIZE) {
|
|
if (src_sector_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) {
|
|
uint8_t *orig = (uint8_t*)(src->hdr + src_sector_offset + pos);
|
|
wb_flash_write(dst, dst_sector_offset + pos, orig, FLASHBUFFER_SIZE);
|
|
}
|
|
pos += FLASHBUFFER_SIZE;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
static int wolfBoot_update(int fallback_allowed)
|
|
{
|
|
uint32_t total_size = 0;
|
|
const uint32_t sector_size = WOLFBOOT_SECTOR_SIZE;
|
|
uint32_t sector = 0;
|
|
uint8_t flag, st;
|
|
struct wolfBoot_image boot, update, swap;
|
|
|
|
/* No Safety check on open: we might be in the middle of a broken update */
|
|
wolfBoot_open_image(&update, PART_UPDATE);
|
|
wolfBoot_open_image(&boot, PART_BOOT);
|
|
wolfBoot_open_image(&swap, PART_SWAP);
|
|
|
|
/* Use biggest size for the swap */
|
|
total_size = boot.fw_size + IMAGE_HEADER_SIZE;
|
|
if ((update.fw_size + IMAGE_HEADER_SIZE) > total_size)
|
|
total_size = update.fw_size + IMAGE_HEADER_SIZE;
|
|
|
|
if (total_size <= IMAGE_HEADER_SIZE)
|
|
return -1;
|
|
|
|
/* Check the first sector to detect interrupted update */
|
|
if ((wolfBoot_get_sector_flag(PART_UPDATE, 0, &flag) < 0) || (flag == SECT_FLAG_NEW))
|
|
{
|
|
uint16_t update_type;
|
|
/* In case this is a new update, do the required
|
|
* checks on the firmware update
|
|
* before starting the swap
|
|
*/
|
|
|
|
update_type = wolfBoot_get_image_type(PART_UPDATE);
|
|
if (((update_type & 0x00FF) != HDR_IMG_TYPE_APP) || ((update_type & 0xFF00) != HDR_IMG_TYPE_AUTH))
|
|
return -1;
|
|
if (!update.hdr_ok || (wolfBoot_verify_integrity(&update) < 0)
|
|
|| (wolfBoot_verify_authenticity(&update) < 0)) {
|
|
return -1;
|
|
}
|
|
#ifndef ALLOW_DOWNGRADE
|
|
if ( !fallback_allowed &&
|
|
(wolfBoot_update_firmware_version() <= wolfBoot_current_firmware_version()) )
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
hal_flash_unlock();
|
|
#ifdef EXT_FLASH
|
|
ext_flash_unlock();
|
|
#endif
|
|
|
|
/* Interruptible swap
|
|
* The status is saved in the sector flags of the update partition.
|
|
* If something goes wrong, the operation will be resumed upon reboot.
|
|
*/
|
|
while ((sector * sector_size) < total_size) {
|
|
if ((wolfBoot_get_sector_flag(PART_UPDATE, sector, &flag) != 0) || (flag == SECT_FLAG_NEW)) {
|
|
flag = SECT_FLAG_SWAPPING;
|
|
wolfBoot_copy_sector(&update, &swap, sector);
|
|
if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE)
|
|
wolfBoot_set_sector_flag(PART_UPDATE, sector, flag);
|
|
}
|
|
if (flag == SECT_FLAG_SWAPPING) {
|
|
uint32_t size = total_size - (sector * sector_size);
|
|
if (size > sector_size)
|
|
size = sector_size;
|
|
flag = SECT_FLAG_BACKUP;
|
|
wolfBoot_copy_sector(&boot, &update, sector);
|
|
if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE)
|
|
wolfBoot_set_sector_flag(PART_UPDATE, sector, flag);
|
|
}
|
|
if (flag == SECT_FLAG_BACKUP) {
|
|
uint32_t size = total_size - (sector * sector_size);
|
|
if (size > sector_size)
|
|
size = sector_size;
|
|
flag = SECT_FLAG_UPDATED;
|
|
wolfBoot_copy_sector(&swap, &boot, sector);
|
|
if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE)
|
|
wolfBoot_set_sector_flag(PART_UPDATE, sector, flag);
|
|
}
|
|
sector++;
|
|
}
|
|
while((sector * sector_size) < WOLFBOOT_PARTITION_SIZE) {
|
|
wb_flash_erase(&boot, sector * sector_size, sector_size);
|
|
wb_flash_erase(&update, sector * sector_size, sector_size);
|
|
sector++;
|
|
}
|
|
wb_flash_erase(&swap, 0, WOLFBOOT_SECTOR_SIZE);
|
|
st = IMG_STATE_TESTING;
|
|
wolfBoot_set_partition_state(PART_BOOT, st);
|
|
#ifdef EXT_FLASH
|
|
ext_flash_lock();
|
|
#endif
|
|
hal_flash_lock();
|
|
return 0;
|
|
}
|
|
|
|
void RAMFUNCTION wolfBoot_start(void)
|
|
{
|
|
uint8_t st;
|
|
struct wolfBoot_image boot, update;
|
|
|
|
#ifdef RAM_CODE
|
|
wolfBoot_check_self_update();
|
|
#endif
|
|
|
|
/* Check if the BOOT partition is still in TESTING,
|
|
* to trigger fallback.
|
|
*/
|
|
if ((wolfBoot_get_partition_state(PART_BOOT, &st) == 0) && (st == IMG_STATE_TESTING)) {
|
|
wolfBoot_update_trigger();
|
|
wolfBoot_update(1);
|
|
/* Check for new updates in the UPDATE partition */
|
|
} else if ((wolfBoot_get_partition_state(PART_UPDATE, &st) == 0) && (st == IMG_STATE_UPDATING)) {
|
|
wolfBoot_update(0);
|
|
}
|
|
if ((wolfBoot_open_image(&boot, PART_BOOT) < 0) ||
|
|
(wolfBoot_verify_integrity(&boot) < 0) ||
|
|
(wolfBoot_verify_authenticity(&boot) < 0)) {
|
|
if (wolfBoot_update(1) < 0) {
|
|
/* panic: no boot option available. */
|
|
while(1)
|
|
;
|
|
} else {
|
|
/* Emergency update successful, try to re-open boot image */
|
|
if ((wolfBoot_open_image(&boot, PART_BOOT) < 0) ||
|
|
(wolfBoot_verify_integrity(&boot) < 0) ||
|
|
(wolfBoot_verify_authenticity(&boot) < 0)) {
|
|
/* panic: something went wrong after the emergency update */
|
|
while(1)
|
|
;
|
|
}
|
|
}
|
|
}
|
|
hal_prepare_boot();
|
|
do_boot((void *)boot.fw_base);
|
|
}
|