mirror of https://github.com/wolfSSL/wolfBoot.git
341 lines
12 KiB
C
341 lines
12 KiB
C
/* delta.c
|
|
*
|
|
* Copyright (C) 2021 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
|
|
*/
|
|
#ifdef DELTA_UPDATES
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <delta.h>
|
|
|
|
|
|
#define ESC 0x7f
|
|
|
|
#if (defined(__IAR_SYSTEMS_ICC__) && (__IAR_SYSTEMS_ICC__ > 8)) || \
|
|
defined(__GNUC__)
|
|
#define BLOCK_HDR_PACKED __attribute__ ((packed))
|
|
#else
|
|
#define BLOCK_HDR_PACKED
|
|
#endif
|
|
|
|
struct BLOCK_HDR_PACKED block_hdr {
|
|
uint8_t esc;
|
|
uint8_t off[3];
|
|
uint8_t sz[2];
|
|
};
|
|
|
|
#define BLOCK_HDR_SIZE (sizeof (struct block_hdr))
|
|
|
|
#if defined(EXT_ENCRYPTED) && defined(__WOLFBOOT)
|
|
#include "encrypt.h"
|
|
#define ext_flash_check_write ext_flash_encrypt_write
|
|
#define ext_flash_check_read ext_flash_decrypt_read
|
|
#else
|
|
#include "hal.h"
|
|
#define ext_flash_check_write ext_flash_write
|
|
#define ext_flash_check_read ext_flash_read
|
|
#endif
|
|
|
|
int wb_patch_init(WB_PATCH_CTX *bm, uint8_t *src, uint32_t ssz, uint8_t *patch,
|
|
uint32_t psz)
|
|
{
|
|
if (!bm || ssz == 0 || psz == 0) {
|
|
return -1;
|
|
}
|
|
memset(bm, 0, sizeof(WB_PATCH_CTX));
|
|
bm->src_base = src;
|
|
bm->src_size = ssz;
|
|
bm->patch_base = patch;
|
|
bm->patch_size = psz;
|
|
#ifdef EXT_FLASH
|
|
bm->patch_cache_start = 0xFFFFFFFF;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#ifdef EXT_FLASH
|
|
#define PATCH_CACHE_SIZE 256
|
|
#define DELTA_SWAP_CACHE_SIZE 1024
|
|
|
|
static inline uint8_t *patch_read_cache(WB_PATCH_CTX *ctx)
|
|
{
|
|
if (ctx->patch_cache_start != 0xFFFFFFFF) {
|
|
if (ctx->patch_cache_start == ctx->p_off)
|
|
return ctx->patch_cache;
|
|
|
|
if (ctx->p_off < ctx->patch_cache_start +
|
|
(DELTA_PATCH_BLOCK_SIZE - BLOCK_HDR_SIZE))
|
|
return ctx->patch_cache + ctx->p_off - ctx->patch_cache_start;
|
|
}
|
|
ctx->patch_cache_start = ctx->p_off;
|
|
ext_flash_check_read(
|
|
(uintptr_t)(ctx->patch_base + ctx->p_off),
|
|
ctx->patch_cache, DELTA_PATCH_BLOCK_SIZE);
|
|
return ctx->patch_cache;
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
static inline uint8_t *patch_read_cache(WB_PATCH_CTX *ctx)
|
|
{
|
|
return ctx->patch_base + ctx->p_off;
|
|
}
|
|
|
|
#endif
|
|
|
|
int wb_patch(WB_PATCH_CTX *ctx, uint8_t *dst, uint32_t len)
|
|
{
|
|
struct block_hdr *hdr;
|
|
uint32_t dst_off = 0;
|
|
uint32_t src_off;
|
|
uint16_t sz;
|
|
uint32_t copy_sz;
|
|
if (!ctx)
|
|
return -1;
|
|
if (len < BLOCK_HDR_SIZE)
|
|
return -1;
|
|
|
|
while ( ( (ctx->matching != 0) || (ctx->p_off < ctx->patch_size)) && (dst_off < len)) {
|
|
uint8_t *pp = patch_read_cache(ctx);
|
|
if (ctx->matching) {
|
|
/* Resume matching block from previous sector */
|
|
sz = ctx->blk_sz;
|
|
if (sz > len)
|
|
sz = len;
|
|
memcpy(dst + dst_off, ctx->src_base + ctx->blk_off, sz);
|
|
if (ctx->blk_sz > len) {
|
|
ctx->blk_sz -= len;
|
|
ctx->blk_off += len;
|
|
} else {
|
|
ctx->blk_off = 0;
|
|
ctx->blk_sz = 0;
|
|
ctx->matching = 0;
|
|
}
|
|
dst_off += sz;
|
|
continue;
|
|
}
|
|
if (*pp == ESC) {
|
|
if (*(pp + 1) == ESC) {
|
|
*(dst + dst_off) = ESC;
|
|
/* Two bytes of the patch have been consumed to produce ESC */
|
|
ctx->p_off += 2;
|
|
dst_off++;
|
|
continue;
|
|
} else {
|
|
hdr = (struct block_hdr *)pp;
|
|
src_off = (hdr->off[0] << 16) + (hdr->off[1] << 8) +
|
|
hdr->off[2];
|
|
sz = (hdr->sz[0] << 8) + hdr->sz[1];
|
|
ctx->matching = 1;
|
|
if (sz > (len - dst_off)) {
|
|
copy_sz = len - dst_off;
|
|
ctx->blk_off = src_off + copy_sz;
|
|
ctx->blk_sz = sz - copy_sz;
|
|
} else {
|
|
copy_sz = sz;
|
|
}
|
|
memcpy(dst + dst_off, ctx->src_base + src_off, copy_sz);
|
|
if (sz == copy_sz) {
|
|
/* End of the block, reset counters and matching state */
|
|
ctx->matching = 0;
|
|
ctx->blk_off = 0;
|
|
}
|
|
ctx->p_off += BLOCK_HDR_SIZE;
|
|
dst_off += copy_sz;
|
|
}
|
|
} else {
|
|
*(dst + dst_off) = *pp;
|
|
dst_off++;
|
|
ctx->p_off++;
|
|
}
|
|
}
|
|
return dst_off;
|
|
}
|
|
|
|
|
|
int wb_diff_init(WB_DIFF_CTX *ctx, uint8_t *src_a, uint32_t len_a, uint8_t *src_b, uint32_t len_b)
|
|
{
|
|
if (!ctx || (len_a == 0) || (len_b == 0))
|
|
return -1;
|
|
memset(ctx, 0, sizeof(WB_DIFF_CTX));
|
|
ctx->src_a = src_a;
|
|
ctx->src_b = src_b;
|
|
ctx->size_a = len_a;
|
|
ctx->size_b = len_b;
|
|
return 0;
|
|
}
|
|
|
|
int wb_diff(WB_DIFF_CTX *ctx, uint8_t *patch, uint32_t len)
|
|
{
|
|
struct block_hdr hdr;
|
|
int found;
|
|
uint8_t *pa, *pb;
|
|
uint16_t match_len;
|
|
uint32_t blk_start;
|
|
uint32_t p_off = 0;
|
|
if (ctx->off_b >= ctx->size_b)
|
|
return 0;
|
|
if (len < BLOCK_HDR_SIZE)
|
|
return -1;
|
|
|
|
while ((ctx->off_b + BLOCK_HDR_SIZE < ctx->size_b) && (len > p_off + BLOCK_HDR_SIZE)) {
|
|
uint32_t page_start = ctx->off_b / WOLFBOOT_SECTOR_SIZE;
|
|
uint32_t pa_start;
|
|
found = 0;
|
|
if (p_off + BLOCK_HDR_SIZE > len)
|
|
return p_off;
|
|
|
|
/* 'A' Patch base is valid for addresses in blocks ahead.
|
|
* For matching previous blocks, 'B' is used as base instead.
|
|
*
|
|
* This mechanism ensures that the patch can refer to lower position
|
|
* in a FLASH memory that is being patched, using the destination as
|
|
* base for the sectors that have already been updated.
|
|
*/
|
|
|
|
pa_start = (WOLFBOOT_SECTOR_SIZE + 1) * page_start;
|
|
pa = ctx->src_a + pa_start;
|
|
while (((uint32_t)(pa - ctx->src_a) < ctx->size_a ) && (p_off < len)) {
|
|
if ((uint32_t)(ctx->size_a - (pa - ctx->src_a)) < BLOCK_HDR_SIZE)
|
|
break;
|
|
if ((ctx->size_b - ctx->off_b) < BLOCK_HDR_SIZE)
|
|
break;
|
|
if ((WOLFBOOT_SECTOR_SIZE - (ctx->off_b % WOLFBOOT_SECTOR_SIZE)) < BLOCK_HDR_SIZE)
|
|
break;
|
|
if ((memcmp(pa, (ctx->src_b + ctx->off_b), BLOCK_HDR_SIZE) == 0)) {
|
|
uint32_t b_start;
|
|
/* Identical areas of BLOCK_HDR_SIZE bytes match between the images.
|
|
* initialize match_len; blk_start is the relative offset within
|
|
* the src image.
|
|
*/
|
|
match_len = BLOCK_HDR_SIZE;
|
|
blk_start = pa - ctx->src_a;
|
|
b_start = ctx->off_b;
|
|
pa+= BLOCK_HDR_SIZE;
|
|
ctx->off_b += BLOCK_HDR_SIZE;
|
|
while (*pa == *(ctx->src_b + ctx->off_b)) {
|
|
/* Extend matching block if possible, as long as the
|
|
* identical sequence continues.
|
|
*/
|
|
if ((uint32_t)(pa + 1 - ctx->src_a) >= ctx->size_a) {
|
|
/* Stop matching if the source image size limit is hit. */
|
|
break;
|
|
}
|
|
if ((b_start / WOLFBOOT_SECTOR_SIZE) < ((ctx->off_b + 1) / WOLFBOOT_SECTOR_SIZE)) {
|
|
/* Stop matching when the sector bound is hit. */
|
|
break;
|
|
}
|
|
/* Increase match len, test next byte */
|
|
pa++;
|
|
ctx->off_b++;
|
|
match_len++;
|
|
}
|
|
hdr.esc = ESC;
|
|
hdr.off[0] = ((blk_start >> 16) & 0x000000FF);
|
|
hdr.off[1] = ((blk_start >> 8) & 0x000000FF);
|
|
hdr.off[2] = ((blk_start) & 0x000000FF);
|
|
hdr.sz[0] = ((match_len >> 8) & 0x00FF);
|
|
hdr.sz[1] = ((match_len) & 0x00FF);
|
|
memcpy(patch + p_off, &hdr, sizeof(hdr));
|
|
p_off += BLOCK_HDR_SIZE;
|
|
found = 1;
|
|
|
|
break;
|
|
} else pa++;
|
|
}
|
|
if (!found) {
|
|
/* Try matching an earlier section in the resulting image */
|
|
uint32_t pb_end = page_start * WOLFBOOT_SECTOR_SIZE;
|
|
pb = ctx->src_b;
|
|
while (((uint32_t)(pb - ctx->src_b) < pb_end) && (p_off < len)) {
|
|
/* Check image boundary */
|
|
if ((ctx->size_b - ctx->off_b) < BLOCK_HDR_SIZE)
|
|
break;
|
|
if ((uint32_t)(ctx->size_b - (pb - ctx->src_b)) < BLOCK_HDR_SIZE)
|
|
break;
|
|
|
|
/* Don't try matching backwards if the distance between the two
|
|
* blocks is smaller than one sector.
|
|
*/
|
|
if (WOLFBOOT_SECTOR_SIZE > (pb - ctx->src_b) - (page_start * WOLFBOOT_SECTOR_SIZE))
|
|
break;
|
|
|
|
if ((memcmp(pb, (ctx->src_b + ctx->off_b), BLOCK_HDR_SIZE) == 0)) {
|
|
/* A match was found between the current pointer and a
|
|
* previously patched area in the resulting image.
|
|
* Initialize match_len and set the blk_start to the beginning
|
|
* of the matching area in the image.
|
|
*/
|
|
match_len = BLOCK_HDR_SIZE;
|
|
blk_start = pb - ctx->src_b;
|
|
pb+= BLOCK_HDR_SIZE;
|
|
ctx->off_b += BLOCK_HDR_SIZE;
|
|
while (*pb == *(ctx->src_b + ctx->off_b)) {
|
|
/* Extend match as long as the areas have the
|
|
* same content. Block skipping in this case is
|
|
* not a problem since the distance between the patched
|
|
* area and the area to patch is always larger than one
|
|
* block size.
|
|
*/
|
|
pb++;
|
|
if ((uint32_t)(pb - ctx->src_b) >= pb_end) {
|
|
pb--;
|
|
break;
|
|
}
|
|
match_len++;
|
|
ctx->off_b++;
|
|
}
|
|
hdr.esc = ESC;
|
|
hdr.off[0] = ((blk_start >> 16) & 0x000000FF);
|
|
hdr.off[1] = ((blk_start >> 8) & 0x000000FF);
|
|
hdr.off[2] = ((blk_start) & 0x000000FF);
|
|
hdr.sz[0] = ((match_len >> 8) & 0x00FF);
|
|
hdr.sz[1] = ((match_len) & 0x00FF);
|
|
memcpy(patch + p_off, &hdr, sizeof(hdr));
|
|
p_off += BLOCK_HDR_SIZE;
|
|
found = 1;
|
|
break;
|
|
} else pb++;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
if (*(ctx->src_b + ctx->off_b) == ESC) {
|
|
*(patch + p_off++) = ESC;
|
|
*(patch + p_off++) = ESC;
|
|
} else {
|
|
*(patch + p_off++) = *(ctx->src_b + ctx->off_b);
|
|
}
|
|
ctx->off_b++;
|
|
}
|
|
}
|
|
while ((p_off < len - BLOCK_HDR_SIZE) && ctx->off_b < ctx->size_b) {
|
|
if (*(ctx->src_b + ctx->off_b) == ESC) {
|
|
*(patch + p_off++) = ESC;
|
|
*(patch + p_off++) = ESC;
|
|
} else {
|
|
*(patch + p_off++) = *(ctx->src_b + ctx->off_b);
|
|
}
|
|
ctx->off_b++;
|
|
}
|
|
return (p_off);
|
|
}
|
|
|
|
#endif /* DELTA_UPDATES */
|