wolfBoot/src/loader.c

186 lines
6.3 KiB
C

/* loader.c
*
* 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 "uart_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;
extern void (** const IV_RAM)(void);
#endif
#ifndef DUALBANK_SWAP
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)) {
static uint8_t buffer[FLASHBUFFER_SIZE];
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_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;
}
#endif
int main(void)
{
hal_init();
spi_flash_probe();
#ifdef UART_FLASH
uart_init(UART_FLASH_BITRATE, 8, 'N', 1);
uart_send_current_version();
#endif
#ifdef WOLFBOOT_TPM
wolfBoot_tpm2_init();
#endif
wolfBoot_start();
while(1)
;
return 0;
}