Files
TencentOS-tiny/components/ota/recovery/ota_patch.c
2020-06-28 19:23:05 +08:00

388 lines
11 KiB
C

/*----------------------------------------------------------------------------
* Tencent is pleased to support the open source community by making TencentOS
* available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* If you have downloaded a copy of the TencentOS binary from Tencent, please
* note that the TencentOS binary is licensed under the BSD 3-Clause License.
*
* If you have downloaded a copy of the TencentOS source code from Tencent,
* please note that TencentOS source code is licensed under the BSD 3-Clause
* License, except for the third-party components listed below which are
* subject to different license terms. Your integration of TencentOS into your
* own projects may require compliance with the BSD 3-Clause License, as well
* as the other licenses applicable to the third-party components included
* within TencentOS.
*---------------------------------------------------------------------------*/
#include "ota_patch.h"
#include "ota_flash.h"
#include "ota_partition.h"
#include "lzma_uncompress.h"
static int32_t offtin_u32(uint8_t *buf)
{
int32_t y;
y = buf[3] & 0x7F;
y = y * 256; y += buf[2];
y = y * 256; y += buf[1];
y = y * 256; y += buf[0];
if (buf[3] & 0x80) {
y = -y;
}
return y;
}
static uint16_t offtin_u16(uint8_t *buf)
{
int32_t y;
y = buf[1];
y = y * 256; y += buf[0];
return y;
}
static int backup_map_create(bkup_map_t *bkup_map, size_t ota_size, uint16_t blk_len)
{
uint16_t i = 0;
uint16_t *cache;
uint16_t remain_blk_cnt;
uint32_t start;
size_t remain_size;
if (ota_partition_size(OTA_PARTITION_OTA) <= ALIGN_UP(ota_size, blk_len)) {
return -1;
}
remain_size = ota_partition_size(OTA_PARTITION_OTA) - ALIGN_UP(ota_size, blk_len);
remain_blk_cnt = BLK_CNT(remain_size, blk_len);
start = ota_partition_start(OTA_PARTITION_OTA) + ALIGN_UP(ota_size, blk_len);
cache = malloc(remain_blk_cnt * sizeof(uint16_t));
if (cache == NULL) {
return -1;
}
for (i = 0; i < remain_blk_cnt; ++i) {
cache[i] = (uint16_t)-1;
}
bkup_map->cache = cache;
bkup_map->backup_n = remain_blk_cnt;
bkup_map->blk_len = blk_len;
bkup_map->start = start;
bkup_map->cursor =0;
return 0;
}
static int backup_map_destroy(bkup_map_t *bkup_map)
{
free(bkup_map->cache);
return 0;
}
static int backup_map_add(bkup_map_t *bkup_map, uint16_t blk_idx, uint32_t backup_ed_addr, uint8_t *blk_buf)
{
uint16_t slot = bkup_map->cursor;
uint16_t blk_len = bkup_map->blk_len;
uint32_t backup_ee_addr = THE_BUF(bkup_map->start, slot, bkup_map->blk_len);
if (ota_flash_erase(backup_ee_addr, blk_len) != 0) {
return -1;
}
if (ota_flash_read(backup_ed_addr, blk_buf, blk_len) != 0) {
return -1;
}
if (ota_flash_write(backup_ee_addr, blk_buf, blk_len) != 0) {
return -1;
}
bkup_map->cache[slot] = blk_idx;
bkup_map->cursor = (slot + 1) % bkup_map->backup_n;
return 0;
}
static query_res_t backup_map_query(bkup_map_t *bkup_map, uint16_t blk_idx, uint8_t *blk_buf)
{
uint16_t i = 0;
uint32_t backup_ee_addr;
uint16_t blk_len = bkup_map->blk_len;
for (i = 0; i < bkup_map->backup_n; ++i) {
if (bkup_map->cache[i] == blk_idx) {
backup_ee_addr = THE_BUF(bkup_map->start, i, blk_len);
if (ota_flash_read(backup_ee_addr, blk_buf, blk_len) != 0) {
return QUERY_RESULT_FETCH_FAILED;
}
return QUERY_RESULT_OK;
}
}
return QUERY_RESULT_NOT_EXIST;
}
int ota_patch(uint32_t patch, size_t patchsize, size_t ota_size)
{
int rc = 0, i = 0;
/* the patch information stuff */
uint8_t *block = NULL, *blk_buf = NULL;
uint8_t *unzipped_patch = NULL, *zipped_patch = NULL;
uint16_t blk_len, blk_cnt;
uint16_t unzipped_len, zipped_len;
size_t the_unzipped_len, the_zipped_len;
/* the backup stuff */
bkup_map_t bkup_map;
query_res_t res;
/* the patch4blk stuff */
uint8_t X;
uint32_t Z;
uint16_t I, N, Y, C, D;
uint8_t *ctrl_blk, *diff_blk, *extra_blk;
uint16_t cursor = 0;
uint16_t old_idx, old_offset;
patch_hdr_t patch_hdr;
patch4blk_hdr_t patch4blk_hdr;
patch4blk_info_t *patch4blk_info;
/*
format of patch:
patch_hdr + (patch4blk_hdr + zipped(patch4blk)) * N
patch_hdr:
0 2 length of one block
4 2 total count of blocks in the patch
patch4blk_hdr:
4 2 original length of the patch
8 2 zipped length of the patch
*/
/* read the patch header */
if (ota_flash_read(patch, &patch_hdr, sizeof(patch_hdr_t)) != 0) {
ERROR();
}
patch += sizeof(patch_hdr_t);
patchsize -= sizeof(patch_hdr_t);
if (patchsize <= 0) {
ERROR();
}
blk_len = patch_hdr.blk_len;
blk_cnt = patch_hdr.blk_cnt;
/* create backup map to cache old blocks */
if (!ota_partition_is_pingpong() &&
backup_map_create(&bkup_map, ota_size, blk_len) != 0) {
ERROR();
}
block = malloc(blk_len);
if (!block) {
ERROR();
}
blk_buf = malloc(blk_len);
if (!blk_buf) {
ERROR();
}
while (blk_cnt--) {
/* read the patch4blk header */
if (ota_flash_read(patch, &patch4blk_hdr, sizeof(patch4blk_hdr_t)) != 0) {
ERROR();
}
patch += sizeof(patch4blk_hdr_t);
patchsize -= sizeof(patch4blk_hdr_t);
if (patchsize <= 0) {
ERROR();
}
unzipped_len = patch4blk_hdr.unzipped_len;
zipped_len = patch4blk_hdr.zipped_len;
the_unzipped_len = unzipped_len;
the_zipped_len = zipped_len;
/* some malloc */
zipped_patch = malloc(the_zipped_len);
if (!zipped_patch) {
ERROR();
}
unzipped_patch = malloc(the_unzipped_len);
if (!unzipped_patch) {
ERROR();
}
/* read the zipped patch4blk */
if (ota_flash_read(patch, zipped_patch, the_zipped_len) != 0) {
ERROR();
}
patch += the_zipped_len;
patchsize -= the_zipped_len;
if (patchsize <= 0) {
ERROR();
}
/* do unzip */
if (lzma_uncompress(unzipped_patch, &the_unzipped_len, zipped_patch, &the_zipped_len) != LZMA_ERR_OK) {
ERROR();
}
free(zipped_patch);
/*
format of patch for one block:
0 2 I block index of the patch
2 2 N count of cmd
4 2 C sizeof(control block)
6 2 D sizeof(diff block)
6 C control block
6 + C D diff block
6 + C + D ? extra block
*/
/* control block:
with a leading 8bit domain, indicating the cmd type, 0 for DIFF, 1 for EXTRA
if current is a CMD_COPY(X, Y, Z) cmd:
X is 0(COPY)
locate old to Z, copy Y bytes from oldfile
if current is a CMD_DIFF(X, Y, Z) cmd:
X is 1(DIFF)
locate old to Z, add Y bytes from oldfile to Y bytes from the diff block
if current is a CMD_EXTRA(X, Y) cmd:
X is 2(EXTRA)
copy Y bytes from the extra block
*/
/* here the show begins */
/* read the patch4info */
patch4blk_info = (patch4blk_info_t *)unzipped_patch;
I = patch4blk_info->blk_idx;
N = patch4blk_info->cmd_cnt;
C = patch4blk_info->ctrl_size;
D = patch4blk_info->diff_size;
ctrl_blk = unzipped_patch + sizeof(patch4blk_info_t);
diff_blk = unzipped_patch + sizeof(patch4blk_info_t) + C;
extra_blk = unzipped_patch + sizeof(patch4blk_info_t) + C + D;
cursor = 0;
memset(block, 0, blk_len);
/* backup the old block I first */
if (!ota_partition_is_pingpong() &&
backup_map_add(&bkup_map, I,
THE_BUF(ota_partition_start(OTA_PARTITION_ACTIVE_APP), I, blk_len),
blk_buf) != 0) {
ERROR();
}
while (N--) {
X = *(uint8_t *)ctrl_blk;
ctrl_blk += 1;
if (X == CMD_COPY || X == CMD_DIFF) { // a COPY or DIFF cmd
Y = offtin_u16(ctrl_blk);
ctrl_blk += sizeof(uint16_t);
Z = offtin_u32(ctrl_blk);
ctrl_blk += sizeof(uint32_t);
old_idx = BLK_IDX(Z, blk_len);
old_offset = Z - BLK_LOWER_BOUND(old_idx, blk_len);
/* sanity check */
if (BLK_IDX(cursor + Y - 1, blk_len) != BLK_IDX(cursor, blk_len)) {
ERROR();
}
if (ota_partition_is_pingpong()) {
// if pingpong, copy from OTA_PARTITION_BACKUP_APP
if (ota_flash_read(THE_BUF(ota_partition_start(OTA_PARTITION_BACKUP_APP), old_idx, blk_len),
blk_buf, blk_len) != 0) {
ERROR();
}
} else {
res = backup_map_query(&bkup_map, old_idx, blk_buf);
if (res == QUERY_RESULT_FETCH_FAILED) {
ERROR();
}
if (res == QUERY_RESULT_NOT_EXIST) {
if (ota_flash_read(THE_BUF(ota_partition_start(OTA_PARTITION_ACTIVE_APP), old_idx, blk_len),
blk_buf, blk_len) != 0) {
ERROR();
}
}
}
memcpy(block + cursor, blk_buf + old_offset, Y);
if (X == CMD_DIFF) {
for (i = 0; i < Y; ++i) {
(block + cursor)[i] += *(int8_t *)(diff_blk + i);
}
diff_blk += Y;
}
} else if (X == CMD_EXTRA) {
Y = offtin_u16(ctrl_blk);
ctrl_blk += sizeof(uint16_t);
/* sanity check */
if (BLK_IDX(cursor + Y - 1, blk_len) != BLK_IDX(cursor, blk_len)) {
ERROR();
}
memcpy(block + cursor, extra_blk, Y);
extra_blk += Y;
} else {
ERROR();
}
cursor += Y;
/* sanity check */
if (cursor > blk_len) {
ERROR();
}
}
free(unzipped_patch);
if (ota_flash_erase(THE_BUF(ota_partition_start(OTA_PARTITION_ACTIVE_APP), I, blk_len), blk_len) != 0) {
ERROR();
}
if (ota_flash_write(THE_BUF(ota_partition_start(OTA_PARTITION_ACTIVE_APP), I, blk_len), block, blk_len) != 0) {
ERROR();
}
}
OUT:
FREE(block);
FREE(blk_buf);
if (!ota_partition_is_pingpong()) {
backup_map_destroy(&bkup_map);
}
return rc;
}