mcu_hi3321_watch/middleware/utils/update/local_update/upg_patch.c
2025-05-26 20:15:20 +08:00

1244 lines
44 KiB
C

/*
* Copyright (c) @CompanyNameMagicTag 2020-2021. All rights reserved.
* @brief FOTA patch application program
*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "securec.h"
#include "common_def.h"
#include "upg_patch_info.h"
#include "upg_alloc.h"
#include "upg_debug.h"
#include "upg_porting.h"
#include "errcode.h"
#include "LzmaDec.h"
#include "upg_common_porting.h"
#include "upg_common.h"
#include "upg_config.h"
#include "upg_patch.h"
#define Z_OK SZ_OK
#define ZIP_STREAM_END 99
#define WORD_WIDTH 4
/* Code style related constants */
#define PATCH_KNVD_STR "KNVD"
#define PATCH_KNVD_STR_LEN 4
#define PAGE_STATUS_BIT_SEARCH_START 1
#define PAGE_STATUS_BIT_SEARCH_END 5
#define PAGE_STATUS_BIT_SEARCH_INC 2
STATIC void fota_patch_free(void *mem)
{
if (mem == NULL) {
return;
}
/* first words of the allocated memory is an int32_t indicating how much memory is allocated... */
int32_t *q = ((int32_t *)mem) - 1;
upg_free(q);
}
STATIC void* fota_patch_alloc(size_t size)
{
volatile int32_t *p = NULL;
uint32_t len = size + (uint32_t)sizeof(int32_t);
p = (int32_t*)upg_malloc(len);
if (p == NULL) {
upg_msg1("upg_malloc failure for requested size = ", size);
return NULL;
}
/* This will panic if it fails so no need to check the return value. */
(void)memset_s((int32_t *)p, len, 0, len);
*p = (int32_t)size;
return (void*)(p + 1);
}
STATIC void* lzma_malloc(ISzAllocPtr p, size_t size)
{
unused(p);
return fota_patch_alloc(size);
}
STATIC void lzma_free(ISzAllocPtr p, void *address)
{
unused(p);
fota_patch_free(address);
}
STATIC errcode_t fota_pkg_flash_read_image(patch *desc, uint32_t length, int32_t location, uint8_t *dest)
{
if (desc->use_plain_text_cache) {
/* Read from the plain text cache. */
uint8_t *src = NULL;
if (desc->image_cache == NULL) {
return ERRCODE_FAIL;
}
src = desc->image_cache + location;
/* This will panic if it fails so no need to check the return value. */
(void)memcpy_s(dest, length, src, length);
} else {
errcode_t ret = ERRCODE_SUCC;
ret = upg_read_old_image_data((uint32_t)location, dest, &length, desc->image_id);
if (ret != ERRCODE_SUCC) {
upg_msg1("fota_pkg_flash_read_image read flash error. ret = ", ret);
return ret;
}
}
return ERRCODE_SUCC;
}
STATIC errcode_t fota_pkg_flash_prep_page_contents_for_write(patch *desc, uint32_t image_page_no,
uint8_t *page_contents)
{
errcode_t ret = ERRCODE_SUCC;
uint8_t *dest = NULL;
if (desc->use_plain_text_cache) {
/* Update the plaintext cache. */
if (desc->image_cache == NULL) {
return ERRCODE_FAIL;
}
dest = desc->image_cache + image_page_no * UPG_FLASH_PAGE_SIZE;
/* This will panic if it fails so no need to check the return value. */
(void)memcpy_s(dest, UPG_FLASH_PAGE_SIZE, page_contents, UPG_FLASH_PAGE_SIZE);
}
return ret;
}
STATIC errcode_t fota_pkg_plaintext_flash_cache_init(patch *desc)
{
errcode_t ret = ERRCODE_SUCC;
if (!desc->use_plain_text_cache) {
return ret;
}
/* 以下为加解密时使用 */
uint32_t image_len = desc->image_flash_length;
upg_msg2("plaintext_flash_cache_init: [flash_offset] - [len] => ", desc->image_flash_offset, image_len);
desc->image_cache = (uint8_t*)upg_malloc(image_len);
upg_msg1("plaintext_flash_cache_init: desc->image_cache = ", (uint32_t)(uintptr_t)desc->image_cache);
if (desc->image_cache == NULL) {
upg_msg0("plaintext_flash_cache_init: malloc failure");
return ERRCODE_MALLOC;
}
/* Cache the Plain Text region. */
uint8_t *dst_addr = desc->image_cache;
uint32_t src_offset = desc->image_flash_offset;
ret = upg_read_old_image_data(src_offset, dst_addr, &image_len, desc->image_id);
upg_msg1("plaintext_flash_cache_init:final rom_lib_copy (success=0x3CA5965A), ret = ", ret);
return ret;
}
STATIC bool read_diff_data_from_ram(const uint8_t *addr, uint8_t *data, uint32_t length)
{
size_t copy_size = length;
/* This will panic if it fails so no need to check the return value. */
(void)memcpy_s(data, length, addr, copy_size);
return true;
}
STATIC bool read_diff_data_to_ram(uint32_t offsets, uint8_t *data, uint32_t length, patch_state_t *state)
{
uint32_t read_len = length;
bool success = true;
memset_s(data, DECOMPRESSION_SIZE, 0, DECOMPRESSION_SIZE);
if (state->desc->patch_contents_ram_copy != 0) {
success = read_diff_data_from_ram((uint8_t *)(uintptr_t)(state->desc->patch_contents_ram_copy + offsets),
data, read_len);
} else {
if (upg_read_fota_pkg_data((uint32_t)state->desc->patch_contents_flash_offset + offsets,
(uint8_t *)data, &read_len) != ERRCODE_SUCC) {
success = false;
}
}
if (!success) {
return false;
}
return true;
}
STATIC errcode_t zip_init_helper(zip_context_t *z, patch_state_t *state)
{
SRes sres;
bool success;
uint32_t i;
uint8_t cdata[LZMA_PROPS_SIZE + HN_LZMA_SIZEOF_IMGSIZE];
/* Configure compressed patch data pointer and copy routine */
z->cdata = (unsigned char *)fota_patch_alloc(DECOMPRESSION_SIZE);
if (z->cdata == NULL) {
state->err_code = ERRCODE_MALLOC;
upg_msg1("z->cdata alloc failure, err_code = ", state->err_code);
return state->err_code;
}
read_diff_data_to_ram(0, z->cdata, DECOMPRESSION_SIZE, state);
/* Decode the properties from the header */
success = read_diff_data_from_ram(z->cdata, cdata, LZMA_PROPS_SIZE + HN_LZMA_SIZEOF_IMGSIZE);
if (!success) {
state->err_code = ERRCODE_FAIL;
fota_patch_free(z->cdata);
upg_msg0("zip_init_helper read header failed!");
return state->err_code;
}
sres = LzmaDec_Allocate(z->dec, cdata, LZMA_PROPS_SIZE, &state->lzma_alloc);
if (sres != SZ_OK) {
state->err_code = ERRCODE_FAIL;
fota_patch_free(z->cdata);
upg_msg0("LzmaDec_Allocate failed!");
return state->err_code;
}
LzmaDec_Init(z->dec);
z->offset = LZMA_PROPS_SIZE;
z->cdata_len = state->desc->patch_contents_len - (LZMA_PROPS_SIZE + HN_LZMA_SIZEOF_IMGSIZE);
z->unpacked_len = 0;
for (i = 0; i < HN_LZMA_SIZEOF_IMGSIZE; i++) {
z->unpacked_len += ((uint32_t)(cdata[(uint32_t)z->offset + i])) << (i * NUM_BITS_PER_BYTE);
}
z->unpacked_so_far = 0;
z->offset += HN_LZMA_SIZEOF_IMGSIZE;
return ERRCODE_SUCC;
}
STATIC zip_context_t *zip_init(patch_state_t *state)
{
errcode_t ret;
zip_context_t *z = fota_patch_alloc(sizeof(zip_context_t));
if (z == NULL) {
state->err_code = ERRCODE_MALLOC;
upg_msg0("zip_init z alloc failed");
return z;
}
z->dec = fota_patch_alloc(sizeof(CLzmaDec));
if (z->dec == NULL) {
state->err_code = ERRCODE_MALLOC;
fota_patch_free(z);
z = NULL;
upg_msg0("zip_init z->dec alloc failed");
return z;
}
LzmaDec_Construct(z->dec);
/* LZMA is absolute trash. Required to prevent a segfault */
z->dec->probs = NULL;
ret = zip_init_helper(z, state);
if (ret != ERRCODE_SUCC) {
fota_patch_free(z->dec);
fota_patch_free(z);
z = NULL;
}
return z;
}
STATIC int32_t zip_mem_read(patch_state_t *state, int32_t *error, zip_context_t *z, unsigned char *dest, int32_t len)
{
SizeT src_len, dest_len;
dest_len = (SizeT)len;
uint32_t done_len = 0;
while (done_len != (uint32_t)len) {
src_len = uapi_min(state->desc->patch_contents_len - (uint32_t)(z->offset), DECOMPRESSION_SIZE);
if (!read_diff_data_to_ram((uint32_t)z->offset, (uint8_t *)z->cdata, (uint32_t)src_len, state)) {
state->err_code = ERRCODE_UPG_FILE_READ_FAIL;
upg_msg0("zip_mem_read: read diff error");
return 0;
}
ELzmaStatus status = 0;
SRes ret;
ret = LzmaDec_DecodeToBuf(z->dec, dest + done_len, &dest_len, z->cdata, &src_len, LZMA_FINISH_ANY, &status);
if (ret != SZ_OK) {
state->err_code = (errcode_t)ret;
upg_msg1("zip_mem_read: Decode error, err_code = ", state->err_code);
return 0;
}
z->offset += (int32_t)src_len;
*error = (status == LZMA_STATUS_NOT_FINISHED) ? Z_OK : ZIP_STREAM_END;
z->unpacked_so_far += dest_len;
done_len += dest_len;
dest_len = (uint32_t)len - done_len;
if (status == LZMA_STATUS_FINISHED_WITH_MARK) {
break;
}
}
return (int32_t)done_len;
}
STATIC void zip_end(patch_state_t *state, zip_context_t *z)
{
LzmaDec_Free(z->dec, &state->lzma_alloc);
fota_patch_free(z->cdata);
fota_patch_free(z->dec);
fota_patch_free(z);
}
STATIC void read_image_block(patch_state_t *state, uint32_t size, int32_t location, uint8_t *dest)
{
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("read_image_block state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return;
}
/* Perform flash reads of the image using injected function to handle any decryption required. */
state->err_code = fota_pkg_flash_read_image(state->desc, size, location, dest);
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("ERROR read_image_block, err_code = ", state->err_code);
return;
}
}
void write_image_block(patch_state_t *state, uint32_t size, int32_t location, const uint8_t *source)
{
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("write_image_block state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return;
}
if (upg_write_new_image_data((uint32_t)location, (uint8_t *)source, &size, state->desc->image_id) != ERRCODE_SUCC) {
state->err_code = ERRCODE_FAIL;
upg_msg1("write_image_block write err_code = ", state->err_code);
return;
}
upg_calculate_and_notify_process((uint32_t)(state->page_last_written + 1));
}
/*
* buffer file contains
* 1. a page of buffer_size
* 2. a set of bytes, one byte per page.
* **N.B.** in a real, flash-based implementation, these bits would have
* to be spaced out in accordance with the flash's access rules.
*/
STATIC bool fota_buffers_has_contents(patch_state_t *state)
{
fota_buffers_t *buffer = NULL;
uint32_t i;
bool buffers_set = true;
errcode_t ret;
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("fota_buffers_has_contents state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return buffers_set;
}
buffer = fota_patch_alloc(sizeof(fota_buffers_t));
if (buffer == NULL) {
state->err_code = ERRCODE_MALLOC;
upg_msg0("fota_buffers_has_contents: alloc buffer failed");
goto ret_free;
}
ret = upg_flash_read(state->desc->buffers_flash_offset, sizeof(fota_buffers_t), (uint8_t*)(uintptr_t)buffer);
if (ret != ERRCODE_SUCC) {
state->err_code = ret;
upg_msg1("fota_buffers_has_contents: cannot read buffers, err_code = ", state->err_code);
goto ret_free;
}
for (i = 0; i < (sizeof(fota_buffers_t) / WORD_WIDTH); i++) {
if (*((uint32_t *)(uintptr_t)buffer + i) != 0xffffffff) {
/* The buffer was not erased */
upg_msg2("fota_buffers_has_contents: fota_buffers_t are not erased [i] - [val] : ",
i, *(((uint32_t*)(uintptr_t)buffer) + i));
goto ret_free;
}
}
/* The buffer has been erased */
buffers_set = false;
ret_free:
fota_patch_free(buffer);
return buffers_set;
}
STATIC void read_page_buffer(patch_state_t *state, uint8_t *dest)
{
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("read_page_buffer state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return;
}
if (upg_flash_read(state->desc->buffers_flash_offset, UPG_FLASH_PAGE_SIZE, dest) != ERRCODE_SUCC) {
state->err_code = ERRCODE_FAIL;
upg_msg1("read_page_buffer: cannot read buffer, err_code = ", state->err_code);
return;
}
}
STATIC void replace_page_buffer(patch_state_t *state, uint8_t *source)
{
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("replace_page_buffer state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return;
}
if (upg_flash_write(state->desc->buffers_flash_offset, UPG_FLASH_PAGE_SIZE, source, true) != ERRCODE_SUCC) {
state->err_code = ERRCODE_FAIL;
upg_msg0("replace_page_buffer: cannot write buffer");
return;
}
}
STATIC uint8_t read_page_status(patch_state_t *state, int32_t page_no)
{
uint32_t val = 0;
uint32_t page_num = state->desc->image_flash_offset / UPG_FLASH_PAGE_SIZE + (uint32_t)page_no;
uint32_t offset =
state->desc->buffers_flash_offset + state->desc->buffers_length + ((uint32_t)sizeof(uint8_t) * page_num);
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("read_page_status state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return 0x00;
}
if (upg_flash_read(offset, sizeof(uint8_t), (uint8_t *)&val) != ERRCODE_SUCC) {
state->err_code = ERRCODE_FAIL;
upg_msg1("read_page_status: cannot read status, err_code = ", state->err_code);
return 0x00;
}
return (uint8_t)val;
}
STATIC void write_page_status(patch_state_t *state, int32_t page_no, uint8_t val)
{
uint32_t page_num = state->desc->image_flash_offset / UPG_FLASH_PAGE_SIZE + (uint32_t)page_no;
uint32_t offset =
state->desc->buffers_flash_offset + state->desc->buffers_length + ((uint32_t)sizeof(val) * page_num);
uint32_t old = 0;
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("write_page_status state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return;
}
if (upg_flash_read(offset, sizeof(val), (uint8_t *)&old) != ERRCODE_SUCC) {
state->err_code = ERRCODE_FAIL;
upg_msg1("write_page_status: cannot read old status, err_code = ", state->err_code);
return;
}
if ((val & ~((uint8_t)old)) != 0) {
upg_msg1("write_page_status: Trying to set bits 0->1 at offset = ", offset);
upg_msg2(" with [old] -> [val]", old, val);
}
if (upg_flash_write(offset, sizeof(val), &val, false) != ERRCODE_SUCC) {
state->err_code = ERRCODE_FAIL;
upg_msg1("write_page_status: cannot write status, err_code = ", state->err_code);
return;
}
}
STATIC void erase_fota_buffers(patch_state_t *state)
{
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("erase_fota_buffers state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return;
}
if (upg_flash_erase(state->desc->buffers_flash_offset, state->desc->buffers_length) != ERRCODE_SUCC) {
state->err_code = ERRCODE_FAIL;
upg_msg1("erase_fota_buffers: cannot erase the fota buffers, err_code = ", state->err_code);
return;
}
}
STATIC void erase_image_flash_page(patch_state_t *state, int32_t image_page)
{
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("erase_image_flash_page state err_code = ", state->err_code);
return;
}
errcode_t ret;
ret = upg_flash_erase(state->desc->image_flash_offset + (image_page * UPG_FLASH_PAGE_SIZE), UPG_FLASH_PAGE_SIZE);
if (ret != ERRCODE_SUCC) {
upg_msg1("read_byte state err_code = ", state->err_code);
state->err_code = ERRCODE_FAIL;
return;
}
}
/*
* In applying an in-place patch, the flash writes get applied a page at a time.
* Sometimes the page gets written to more than once. In fact, an individual page can get
* written to up to 3 times. (Ok, there may be several updates within a page. But there
* will only be a maximum of 3 writes.
*
* here's a page:
* d d d d d d d d d d d d d d d d d d d
* here's the 3 types of write operations, a b and c
* a5 a4 a3 a2 a1 b1 b2 b3 b4 b5 b6 b7 b8 b9 c4 c3 c2 c2 c1
* (<--------) <----------a b---->----->->----------> <-----------c (<--------)
*
* so this function page_write_type() spots these different operations based on the first and
* last value used.
*/
STATIC unsigned char page_write_type(const patch_state_t *state, int32_t first, int32_t last)
{
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("page_write_type state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return 0xff;
}
const int32_t limit = UPG_FLASH_PAGE_SIZE - 1;
if (((first == 0) && (last == limit)) || ((first == limit) && (last == 0))) {
/* it's a full page. */
upg_msg0("STANDARD A");
return PAGE_WRITE_STANDARD;
}
if ((first > 0) && (first < limit) && (last > 0) && (last < limit)) {
/* it's a section from the middle */
upg_msg0("STANDARD B");
return PAGE_WRITE_STANDARD;
}
if ((first == 0) || (last == 0)) {
upg_msg0("PARTIAL_START");
return PAGE_WRITE_PARTIAL_START;
}
return PAGE_WRITE_PARTIAL_END;
}
STATIC void write_flash_page_skip_done(patch_state_t *state)
{
if (state == NULL || state->err_code != ERRCODE_SUCC || state->desc == NULL) {
upg_msg0("write_flash_page_skip_done state error");
/* Do not process anything if an error occurred */
return;
}
if (state->local_buffer_page != -1) {
const int32_t r = read_page_status(state, state->local_buffer_page);
const int32_t t = page_write_type(state, state->page_first_written, state->page_last_written);
if (((uint32_t)r & (uint32_t)t) == 0) {
upg_calculate_and_notify_process(UPG_FLASH_PAGE_SIZE);
upg_msg1("Skipping! page = ", state->local_buffer_page);
} else if (((uint32_t)r & ((uint32_t)t >> 1)) == 0) {
/* So this means the W bit for the operation was set, but B was cleared. We should never be */
/* in this state; the recovery operation should mark this as completed. */
upg_msg2("Page write bits are not as expected.[page] - [status] => ",
state->local_buffer_page,
read_page_status(state, state->local_buffer_page));
} else {
const uint32_t unsigned_t = (uint32_t) t;
const uint32_t unsigned_r = (uint32_t) r;
const uint32_t val = unsigned_r & ~(unsigned_t>>1);
/* Prepare the image page contents for writing performing any encryption necessary. */
/* Any error returned put into the state err_code to be handled. */
state->err_code = fota_pkg_flash_prep_page_contents_for_write(state->desc,
(uint32_t)state->local_buffer_page,
state->local_buffer);
state->done_skipping = 1;
replace_page_buffer(state, state->local_buffer);
/* mark the bits to show the buffer is good. (Clear B) */
write_page_status(state, state->local_buffer_page, (uint8_t)val);
/* write the whole UPG_FLASH_PAGE_SIZE block */
write_image_block(state, UPG_FLASH_PAGE_SIZE,
state->local_buffer_page * UPG_FLASH_PAGE_SIZE, state->local_buffer);
if (state->err_code == ERRCODE_SUCC) {
/* mark the bits to show it's written. (Clear W (and B)) */
write_page_status(state, state->local_buffer_page,
(uint8_t)(unsigned_r & ~(unsigned_t | (unsigned_t >> 1))));
}
}
}
}
/*
* Note, in order to do the recovery thing, write_byte will SKIP those pages that
* have already been written, in an 'restartable' manner
* to permit recovery from an incomplete update operation.
*/
STATIC void write_byte(patch_state_t *state, int32_t dest_offset, unsigned char val)
{
int32_t dest_offset_page;
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("write_byte state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return;
}
dest_offset_page = dest_offset / UPG_FLASH_PAGE_SIZE;
if (dest_offset_page != state->local_buffer_page) {
/* write back the old page */
write_flash_page_skip_done(state);
/* check to see if we have a page to write back, AND if we have written this already. */
/* now populate state->local_buffer with the next page's values. */
read_image_block(state, UPG_FLASH_PAGE_SIZE, dest_offset_page * UPG_FLASH_PAGE_SIZE, state->local_buffer);
state->local_buffer_page = dest_offset_page;
state->page_first_written = dest_offset % UPG_FLASH_PAGE_SIZE;
}
state->local_buffer[dest_offset % UPG_FLASH_PAGE_SIZE] = val;
state->page_last_written = dest_offset % UPG_FLASH_PAGE_SIZE;
}
STATIC unsigned char read_byte(patch_state_t *state, int32_t dest_offset)
{
int32_t dest_offset_page;
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("read_byte state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return 0xff;
}
dest_offset_page = dest_offset / UPG_FLASH_PAGE_SIZE;
if (dest_offset_page == state->local_buffer_page) {
return state->local_buffer[dest_offset % UPG_FLASH_PAGE_SIZE];
} else {
uint32_t b = 0;
read_image_block(state, 1, dest_offset, (uint8_t *)&b);
return (uint8_t)b;
}
}
STATIC void finish_write(patch_state_t *state)
{
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("finish_write state err_code = ", state->err_code);
/* Do not process anything if an error occurred */
return;
}
write_flash_page_skip_done(state);
state->local_buffer_page = -1;
}
/**
* @brief Sanity check the diff after obtaining a control block.
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
* @return true if processing should proceed with this control block or false if processing should stop.
*/
STATIC bool apply_patch_get_control_block_sanity_check(const apply_patch_state_t *aps, patch_state_t *state)
{
if (aps->newpos + aps->cb.copy > (uint32_t)(state->desc->newsize)) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
upg_msg0("Corrupt patch 4");
return false;
}
if (aps->oldpos + aps->cb.copy > (uint32_t)(state->desc->maxsize)) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
upg_msg0("Corrupt patch 4a");
return false;
}
return true;
}
/**
* @brief Fetch the control block from the diff.
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
* @return true if processing should proceed with this control block or false if processing should stop.
*/
STATIC bool apply_patch_get_control_block(apply_patch_state_t *aps, patch_state_t *state)
{
/* Read control data */
aps->lenread =
(uint32_t)zip_mem_read(state, &(aps->cerror), aps->zcontext, (unsigned char *)&(aps->cb), sizeof(aps->cb));
if ((aps->lenread < sizeof(aps->cb)) || ((aps->cerror != Z_OK) && (aps->cerror != ZIP_STREAM_END))) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
upg_msg0("data exhausted");
return false;
}
if (memcmp((unsigned char *)&(aps->cb), PATCH_KNVD_STR, PATCH_KNVD_STR_LEN) != 0) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
upg_msg0("not magic number");
return false;
}
aps->blockcount++;
/* Sanity-check */
return apply_patch_get_control_block_sanity_check(aps, state);
}
/**
* @brief Apply a delta chunk for a bottom up patch
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
*/
STATIC void apply_patch_delta_chunk_bottom_up(apply_patch_state_t *aps, patch_state_t *state)
{
int32_t newsize = state->desc->newsize;
int32_t chunk_offset;
int32_t rpos;
int32_t wpos;
if (aps->newpos <= aps->oldpos) {
for (chunk_offset = 0; (chunk_offset < aps->chunk_size) && (state->err_code == ERRCODE_SUCC);
chunk_offset++) {
rpos = newsize - 1 - (aps->oldpos + chunk_offset + aps->chunk_start);
wpos = newsize - 1 - (aps->newpos + chunk_offset + aps->chunk_start);
write_byte(state, wpos, read_byte(state, rpos) + aps->unpackedbuf[chunk_offset]);
}
} else {
for (chunk_offset = 0; (chunk_offset < aps->chunk_size) && (state->err_code == ERRCODE_SUCC);
chunk_offset++) {
rpos = newsize - 1 - (aps->oldpos + ((int32_t)aps->cb.copy - 1 - (chunk_offset + aps->chunk_start)));
wpos = newsize - 1 - (aps->newpos + ((int32_t)aps->cb.copy - 1 - (chunk_offset + aps->chunk_start)));
write_byte(state, wpos, read_byte(state, rpos) + aps->unpackedbuf[chunk_offset]);
}
}
}
/**
* @brief Apply a delta chunk for a top down patch
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
*/
STATIC void apply_patch_delta_chunk_top_down(apply_patch_state_t *aps, patch_state_t *state)
{
int32_t chunk_offset;
int32_t rpos;
int32_t wpos;
if (aps->newpos <= aps->oldpos) {
for (chunk_offset = 0; (chunk_offset < aps->chunk_size) && (state->err_code == ERRCODE_SUCC);
chunk_offset++) {
rpos = aps->oldpos + chunk_offset + aps->chunk_start;
wpos = aps->newpos + chunk_offset + aps->chunk_start;
write_byte(state, wpos, read_byte(state, rpos) + aps->unpackedbuf[chunk_offset]);
}
} else {
for (chunk_offset = 0; (chunk_offset < aps->chunk_size) && (state->err_code == ERRCODE_SUCC);
chunk_offset++) {
rpos = aps->oldpos + ((int32_t)aps->cb.copy - 1 - (chunk_offset + aps->chunk_start));
wpos = aps->newpos + ((int32_t)aps->cb.copy - 1 - (chunk_offset + aps->chunk_start));
write_byte(state, wpos, read_byte(state, rpos) + aps->unpackedbuf[chunk_offset]);
}
}
}
/**
* @brief Process the code deltas within the diff for a section of the image.
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
* @return true if processing should be continue onto the next chunk or false if processing should stop.
*/
STATIC bool apply_patch_delta_chunk(apply_patch_state_t *aps, patch_state_t *state, int32_t *cctotal)
{
aps->chunk_size = uapi_min((int32_t)DECOMPRESSION_SIZE, (int32_t)aps->cb.copy - aps->chunk_start);
*cctotal += aps->chunk_size;
aps->unpackedbuf = fota_patch_alloc((uint32_t)aps->chunk_size);
if (aps->unpackedbuf == NULL) {
state->err_code = ERRCODE_MALLOC;
upg_msg1("malloc failure, err_code = ", state->err_code);
/* Exit loop to immediately handle the error */
return false;
}
aps->lenread = (uint32_t)zip_mem_read(state, (int32_t *)&(aps->cerror), aps->zcontext, aps->unpackedbuf,
aps->chunk_size);
if (((int32_t)aps->lenread < aps->chunk_size) ||
((aps->cerror != Z_OK) && (aps->cerror != ZIP_STREAM_END))) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
fota_patch_free(aps->unpackedbuf);
aps->unpackedbuf = NULL;
upg_msg0("data exhausted");
/* Exit loop to immediately handle the error */
return false;
}
/* add diff data to previous data. It's IN PLACE!!!!! */
if (state->desc->bottom_up) {
apply_patch_delta_chunk_bottom_up(aps, state);
} else {
apply_patch_delta_chunk_top_down(aps, state);
}
/* Free any previously allocated memory */
fota_patch_free(aps->unpackedbuf);
aps->unpackedbuf = NULL;
upg_watchdog_kick();
return true;
}
/**
* @brief Process the code deltas within the diff for a section of code.
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
* @return true if processing should be continued or false if processing should stop.
*/
STATIC bool apply_patch_deltas(apply_patch_state_t *aps, patch_state_t *state)
{
int32_t cctotal = 0;
for (aps->chunk_start = 0; (aps->chunk_start < (int32_t)(aps->cb.copy)) && (state->err_code == ERRCODE_SUCC);
aps->chunk_start += (int32_t)DECOMPRESSION_SIZE) {
if (!apply_patch_delta_chunk(aps, state, &cctotal)) {
break;
}
}
/* Ensure that any allocated memory has been freed even if there has been an error */
fota_patch_free(aps->unpackedbuf);
aps->unpackedbuf = NULL;
/* return if there has been an error */
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("apply_patch b state err_code = ", state->err_code);
return false;
}
/* Adjust pointers */
aps->newpos += (int32_t)aps->cb.copy;
aps->oldpos += (int32_t)aps->cb.copy;
/* Sanity-check */
if ((aps->newpos + (int32_t)aps->cb.extra) > state->desc->newsize) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
upg_msg0("newsize error");
return false;
}
return true;
}
/**
* @brief Apply an extra chunk for a bottom up patch
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
*/
STATIC void apply_patch_extra_chunk_bottom_up(apply_patch_state_t *aps, patch_state_t *state)
{
int32_t newsize = state->desc->newsize;
int32_t chunk_offset;
int32_t wpos;
for (chunk_offset = 0; (chunk_offset < aps->chunk_size) && (state->err_code == ERRCODE_SUCC);
chunk_offset++) {
wpos = newsize - 1 - (aps->newpos + chunk_offset + aps->chunk_start);
write_byte(state, wpos, aps->unpackedbuf[chunk_offset]);
}
}
/**
* @brief Apply an extra chunk for a top down patch
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
*/
STATIC void apply_patch_extra_chunk_top_down(apply_patch_state_t *aps, patch_state_t *state)
{
int32_t chunk_offset;
int32_t wpos;
for (chunk_offset = 0; (chunk_offset < aps->chunk_size) && (state->err_code == ERRCODE_SUCC);
chunk_offset++) {
wpos = aps->newpos + chunk_offset + aps->chunk_start;
write_byte(state, wpos, aps->unpackedbuf[chunk_offset]);
}
}
/**
* @brief Process the code extras within the diff for a section of code.
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
* @return true if processing should be continued or false if processing should stop.
*/
STATIC bool apply_patch_extra_chunk(apply_patch_state_t *aps, patch_state_t *state)
{
aps->chunk_size = uapi_min((int32_t)DECOMPRESSION_SIZE, (int32_t)aps->cb.extra - aps->chunk_start);
aps->unpackedbuf = fota_patch_alloc((uint32_t)aps->chunk_size);
if (aps->unpackedbuf == NULL) {
state->err_code = ERRCODE_MALLOC;
upg_msg0("malloc failure");
return false;
}
aps->lenread = (uint32_t)zip_mem_read(state, &(aps->cerror), aps->zcontext, aps->unpackedbuf, aps->chunk_size);
if ((int32_t)aps->lenread < aps->chunk_size) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
fota_patch_free(aps->unpackedbuf);
aps->unpackedbuf = NULL;
upg_msg0("data exhausted");
return false;
}
if (((aps->cerror != Z_OK) && (aps->cerror != ZIP_STREAM_END))) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
upg_msg1("returned(data corrupted) = ", aps->cerror);
}
if (state->desc->bottom_up) {
apply_patch_extra_chunk_bottom_up(aps, state);
} else {
apply_patch_extra_chunk_top_down(aps, state);
}
fota_patch_free(aps->unpackedbuf);
aps->unpackedbuf = NULL;
return true;
}
/**
* @brief Process the code extras within the diff for a section of code.
*
* @param aps apply_patch(...) function state.
* @param state fota patching state.
* @return true if processing should be continued or false if processing should stop.
*/
STATIC bool apply_patch_extras(apply_patch_state_t *aps, patch_state_t *state)
{
for (aps->chunk_start = 0; (aps->chunk_start < (int32_t)(aps->cb.extra)) && (state->err_code == ERRCODE_SUCC);
aps->chunk_start += (int32_t)DECOMPRESSION_SIZE) {
if (!apply_patch_extra_chunk(aps, state)) {
break;
}
}
/* Ensure that any allocated memory has been freed even if there has been an error */
fota_patch_free(aps->unpackedbuf);
aps->unpackedbuf = NULL;
/* return if there has been an error */
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("apply_patch c state err_code = ", state->err_code);
return false;
}
/* Adjust pointers */
aps->newpos += (int32_t)aps->cb.extra;
aps->oldpos += aps->cb.seek;
/* more sanity */
if (aps->newpos > state->desc->newsize) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
upg_msg0("overran destination");
return false;
}
if (aps->oldpos >= (state->desc->maxsize)) {
state->err_code = ERRCODE_UPG_CHECK_FOTA_ERROR;
upg_msg0("overran source");
return false;
}
return true;
}
STATIC void apply_patch(patch_state_t *state)
{
apply_patch_state_t aps;
if (state == NULL || state->err_code != ERRCODE_SUCC || state->desc == NULL) {
upg_msg0("apply_patch state error");
/* Do not process anything if an error occurred */
return;
}
aps.zcontext = zip_init(state);
if ((aps.zcontext == NULL) || (state->err_code != ERRCODE_SUCC)) {
upg_msg1("aps.zcontext init state err_code = ", state->err_code);
return;
}
aps.oldpos = 0;
aps.newpos = 0;
aps.blockcount = 0;
aps.unpackedbuf = NULL;
/* main patching loop */
while ((aps.newpos < state->desc->newsize) && (state->err_code == ERRCODE_SUCC)) {
/* Get the next control block */
if (!apply_patch_get_control_block(&aps, state)) {
break;
}
/* Apply diff deltas */
if (!apply_patch_deltas(&aps, state)) {
break;
}
/* Read extra string */
if (!apply_patch_extras(&aps, state)) {
break;
}
};
/* complete the write */
finish_write(state);
/* Ensure that any allocated memory has been freed even if there has been an error */
fota_patch_free(aps.unpackedbuf);
if (aps.zcontext != NULL) {
/* Clean up the compressed reads */
zip_end(state, aps.zcontext);
}
}
STATIC void erase_redundant_pages(patch_state_t *state)
{
/* Erase any old image pages if new image is smaller */
uint32_t num_old_pages = state->desc->num_old_pages;
uint32_t num_new_pages = state->desc->num_new_pages;
if (num_old_pages > num_new_pages) {
for (uint32_t page = num_new_pages; page < num_old_pages; page++) {
erase_image_flash_page(state, (int32_t)page);
}
}
}
STATIC void process_patch_init(patch_state_t *state, patch *desc, process_patch_state_t *pps)
{
state->desc = desc;
state->lzma_alloc.Alloc = lzma_malloc;
state->lzma_alloc.Free = lzma_free;
state->local_buffer = fota_patch_alloc(UPG_FLASH_PAGE_SIZE);
if (state->local_buffer == NULL) {
upg_msg0("local_buffer malloc error");
state->err_code = ERRCODE_MALLOC;
} else {
state->err_code = ERRCODE_SUCC;
}
state->local_buffer_page = -1;
state->page_first_written = -1;
state->page_last_written = -1;
state->done_skipping = false;
pps->recovery_buffer = NULL;
pps->recovery_found = false;
}
STATIC void process_patch_recover_page(patch_state_t *state, process_patch_state_t *pps, uint32_t page_no,
int32_t page_status, int32_t status_bit)
{
if (pps->recovery_found) {
state->err_code = ERRCODE_FAIL;
upg_msg2("more than one page in progress,[page] - [bit] : ", page_no, status_bit);
return;
}
pps->recovery_buffer = fota_patch_alloc(UPG_FLASH_PAGE_SIZE);
if (pps->recovery_buffer == NULL) {
state->err_code = ERRCODE_MALLOC;
upg_msg0("malloc failed");
return;
}
/* extract contents to recovery_buffer */
read_page_buffer(state, pps->recovery_buffer);
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("process_patch read_page_buffer err_code = ", state->err_code);
fota_patch_free(pps->recovery_buffer);
pps->recovery_buffer = NULL;
return;
}
/* write page */
write_image_block(state, UPG_FLASH_PAGE_SIZE, (int32_t)page_no * UPG_FLASH_PAGE_SIZE, pps->recovery_buffer);
fota_patch_free(pps->recovery_buffer);
pps->recovery_buffer = NULL;
const uint32_t page_status_unsigned = (uint32_t) page_status;
const uint32_t status_bit_unsigned = (uint32_t) status_bit;
/* it's the new page now, so clear down this operation. */
write_page_status(state, (int32_t)page_no,
(uint8_t)(page_status_unsigned & ~((1UL << status_bit_unsigned) | (1UL << (status_bit_unsigned - 1)))));
pps->recovery_found = true;
}
STATIC void process_patch_check_page_buffer_recovery(patch_state_t *state, process_patch_state_t *pps)
{
int32_t page_no;
int32_t status_bit;
/* pass through the bits, and if there's a buffer page written, with the corresponding page deleted,
* then write the buffer to the duff page. */
for (page_no = 0; (page_no < (int32_t)state->desc->num_maxsize_pages) && (state->err_code == ERRCODE_SUCC);
page_no++) {
const int32_t page_status = read_page_status(state, page_no);
const uint32_t page_status_unsigned = (uint32_t)page_status;
for (status_bit = PAGE_STATUS_BIT_SEARCH_START;
(status_bit <= PAGE_STATUS_BIT_SEARCH_END) && (state->err_code == ERRCODE_SUCC);
status_bit += PAGE_STATUS_BIT_SEARCH_INC) {
const uint32_t status_bit_unsigned = (uint32_t)status_bit;
if (((page_status_unsigned & (1UL << status_bit_unsigned)) != 0) &&
((page_status_unsigned & (1UL << (status_bit_unsigned - 1))) == 0)) {
process_patch_recover_page(state, pps, (uint32_t)page_no, page_status, status_bit);
}
}
}
}
STATIC void process_patch_perform_recovery(patch_state_t *state, process_patch_state_t *pps)
{
/* Cache the recovery image in RAM if required, determine if old signature page is encrypted */
state->err_code = fota_pkg_plaintext_flash_cache_init(state->desc);
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("process_patch - caching recovery flash failure err_code = ", state->err_code);
return;
}
/* Check if there is a page buffer written that needs to be written back to flash. */
process_patch_check_page_buffer_recovery(state, pps);
if (state->err_code != ERRCODE_SUCC) {
upg_msg1("process_patch state err_code = ", state->err_code);
return;
}
#if (UPG_CFG_DEBUG_PRINT_ENABLED == YES)
if (!pps->recovery_found) {
upg_msg0("No buffer page required recovery.");
} else {
upg_msg0("Ready to continue patching");
}
upg_msg0("Recovery complete");
#endif
}
errcode_t process_patch(patch *desc)
{
patch_state_t state;
process_patch_state_t pps;
process_patch_init(&state, desc, &pps);
if (state.err_code != ERRCODE_SUCC) {
return state.err_code;
}
/* check if there is a patch in progress and continue it if so. */
if (fota_buffers_has_contents(&state)) {
process_patch_perform_recovery(&state, &pps);
if (state.err_code != ERRCODE_SUCC) {
goto ret_free;
}
} else {
/* Cache non-recovery SEMAIN as required */
state.err_code = fota_pkg_plaintext_flash_cache_init(desc);
if (state.err_code != ERRCODE_SUCC) {
upg_msg1("process_patch - caching flash failure err_code = ", state.err_code);
goto ret_free;
}
erase_fota_buffers(&state);
}
/* apply the zipped patch. This will in turn use the write_byte_skip_done functions
* which means an interrupted write should resume and (potentially) complete ok. */
apply_patch(&state);
erase_redundant_pages(&state);
erase_fota_buffers(&state);
ret_free:
fota_patch_free(state.local_buffer);
fota_patch_free(pps.recovery_buffer);
return state.err_code;
}
STATIC void fota_pkg_task_code_diff_cleanup_actions(patch *patch_desc)
{
/* Clean allocated cache, Freeing a NULL is benign so no check for FOTA_PKG_TASK_ID_SEMAIN_CODE needed */
upg_free(patch_desc->image_cache);
patch_desc->image_cache = NULL;
}
STATIC void init_patch_description_with_task_info(const upg_image_header_t *image, patch *patch_desc)
{
/* This will panic if it fails so no need to check the return value. */
(void)memset_s(patch_desc, sizeof(patch), 0, sizeof(patch));
patch_desc->image_id = image->image_id;
patch_desc->maxsize = uapi_max((int32_t)image->new_image_len, (int32_t)image->old_image_len);
patch_desc->newsize = (int32_t)image->new_image_len;
patch_desc->oldsize = (int32_t)image->old_image_len;
patch_desc->num_new_pages = (uint32_t)(patch_desc->newsize + (UPG_FLASH_PAGE_SIZE - 1)) / UPG_FLASH_PAGE_SIZE;
patch_desc->num_old_pages = (uint32_t)(patch_desc->oldsize + (UPG_FLASH_PAGE_SIZE - 1)) / UPG_FLASH_PAGE_SIZE;
patch_desc->num_maxsize_pages = uapi_max(patch_desc->num_new_pages, patch_desc->num_old_pages);
patch_desc->new_sig_page = patch_desc->num_new_pages - 1;
patch_desc->old_sig_page = patch_desc->num_old_pages - 1;
patch_desc->patch_contents_ram_copy = 0;
patch_desc->patch_contents_flash_offset = (uint32_t)image->image_offset;
patch_desc->patch_contents_len = image->image_len;
patch_desc->bottom_up = image->old_image_len < image->new_image_len;
patch_desc->image_encrypted = false;
/* No failure injection */
patch_desc->failpoint = 0;
patch_desc->failfn = NULL;
/* Image read and prepare for writing functions. */
patch_desc->fetch_image_contents_fn = fota_pkg_flash_read_image;
patch_desc->prep_image_contents_for_write_fn = fota_pkg_flash_prep_page_contents_for_write;
patch_desc->plaintext_flash_cache_init_fn = fota_pkg_plaintext_flash_cache_init;
patch_desc->copy_recovered_buffer_to_flash_cache_fn = NULL;
patch_desc->encrypt_flash_page_fn = NULL;
}
errcode_t fota_pkg_task_apply_code_diff(const upg_image_header_t *image)
{
errcode_t ret;
patch *desc;
uint32_t fota_data_addr;
uint32_t fota_data_len;
if (image == NULL) {
upg_msg0("task_apply_code_diff image is NULL");
return ERRCODE_FAIL;
}
desc = fota_patch_alloc(sizeof(patch));
if (desc == NULL) {
upg_msg0("patch *desc malloc failed");
return ERRCODE_MALLOC;
}
init_patch_description_with_task_info(image, desc);
ret = upg_get_partition_info(image->image_id, &desc->image_flash_offset, &desc->image_flash_length);
if (ret != ERRCODE_SUCC) {
fota_patch_free(desc);
return ret;
}
/* Get the metadata buffer area */
ret = upg_get_progress_status_start_addr(&fota_data_addr, &fota_data_len);
if (ret != ERRCODE_SUCC) {
upg_msg1("get area addr faile, ret = ", ret);
fota_patch_free(desc);
return ret;
}
desc->buffers_flash_offset = fota_data_addr;
desc->buffers_length = FOTA_DATA_BUFFER_AREA_LEN;
/* Update patch description structure with task specific parameters and run through
* any required initialisation functions done on a per task basis */
if (ret == ERRCODE_SUCC) {
/* Attempt to apply the patch */
ret = process_patch(desc);
}
fota_pkg_task_code_diff_cleanup_actions(desc);
fota_patch_free(desc);
return ret;
}