inwudriver-weibo/boot/flashboot/nvm/soc_fnv.c

619 lines
18 KiB
C
Raw Permalink Normal View History

/* *
* Copyright (c) CompanyNameMagicTag 2018-2019. All rights reserved.
* Description: factory nv
* Author: CompanyName
* Create: 2018-10-31
*/
#include <soc_fnv.h>
#include <soc_flashboot_flash.h>
#include <soc_boot_rom.h>
#include <crc32.h>
#ifdef DEBUG_FNV
#define nv_print_f0(s) boot_msg0(s)
#define nv_print_f1(s, v1) boot_msg1(s, v1)
#define nv_print_f2(s, v1, v2) boot_msg2(s, v1, v2)
#else
#define nv_print_f0(s)
#define nv_print_f1(s, v1)
#define nv_print_f2(s, v1, v2)
#endif
/* maximum count of nv ( modified by codex) */
#define NV_TOTAL_NUM_MAX 256
/* factory and normal seperated */
static ext_nv_ctrl g_nv_ctrl[EXT_TYPE_NV_MAX] = {
0,
};
/* repair the nv file correspondence with dst_data address */
EXT_PRV td_void nv_repair_file(td_u8 *dst_data, const td_u8 *src_data, td_u32 len, td_u32 seq)
{
ext_nv_manage *nv_head = (ext_nv_manage *)dst_data;
if (memcpy_s(dst_data, HNV_BLOCK_SIZE, src_data, len) != EXT_ERR_SUCCESS) {
return;
}
nv_head->seq = seq;
nv_head->crc = uapi_crc32(0, dst_data + 8, HNV_CRC_SIZE); /* data shifts 8 */
}
/* nv inner interface: write a fixed address, then change to read from factory nv */
EXT_PRV td_u32 nv_repair_from_backup(td_u8 *pdata1, td_u32 flash_addr, td_u32 src_file_addr)
{
td_u32 ret;
ret = uapi_flash_read(src_file_addr, HNV_BLOCK_SIZE, pdata1);
if (ret != EXT_ERR_SUCCESS) {
return ret;
}
ret = uapi_flash_erase(flash_addr, HNV_BLOCK_SIZE);
if (ret != EXT_ERR_SUCCESS) {
return ret;
}
ret = uapi_flash_write(flash_addr, HNV_BLOCK_SIZE, pdata1, TD_FALSE);
if (ret != EXT_ERR_SUCCESS) {
return ret;
}
return ret;
}
/* check the legality of nv file */
EXT_PRV td_u32 nv_check_file(const td_u8 *pdata, td_u32 len, ext_nv_type nv_type)
{
ext_nv_manage *nv_head = TD_NULL;
td_u32 magic = FNV_FILE_SIGNATURE;
nv_head = (ext_nv_manage *)pdata;
if (nv_type != EXT_TYPE_FACTORY_NV) {
magic = HNV_FILE_SIGNATURE;
}
if (magic != nv_head->magic) {
return EXT_ERR_MAGIC_CHECK;
}
if (nv_head->total_num > NV_TOTAL_NUM_MAX) {
return EXT_ERR_INVALID_PARAMETER;
}
if (nv_head->crc != uapi_crc32(0, pdata + 8, len - 8)) { /* shifts 8 */
return EXT_ERR_CRC_CHECK;
}
return EXT_ERR_SUCCESS;
}
/* return nv index */
EXT_PRV ext_nv_item_index *nv_find_item(const td_u8 *pdata, td_u8 id, ext_nv_type nv_type)
{
ext_nv_manage *nv_head = TD_NULL;
ext_nv_item_index *index = TD_NULL;
td_u16 total_num;
if (pdata == TD_NULL) {
total_num = g_nv_ctrl[nv_type].total_num;
index = g_nv_ctrl[nv_type].index;
} else {
nv_head = (ext_nv_manage *)pdata;
total_num = nv_head->total_num;
index = (ext_nv_item_index *)&nv_head->nv_item_data[0];
}
for (int i = 0; i < total_num; i++) {
if (id == index[i].nv_id) {
return &index[i];
}
}
return (ext_nv_item_index *)TD_NULL;
}
/* stage of data prepare */
EXT_PRV td_u32 nv_init_data_prepare(td_u8 **pdata, td_u32 flash_addr)
{
td_u32 ret;
td_u8 *data = TD_NULL;
data = boot_malloc(HNV_BLOCK_SIZE);
if (data == TD_NULL) {
ret = EXT_ERR_MALLOC_FAILUE;
return ret;
}
ret = uapi_flash_read(flash_addr, HNV_BLOCK_SIZE, data);
if (ret != EXT_ERR_SUCCESS) {
boot_free(data);
return ret;
}
*pdata = data;
return ret;
}
EXT_PRV td_void nv_init_data_check_all_success(const td_u8 *pdata1, const td_u8 *pdata2, td_u32 flash_addr,
ext_nv_ctrl *nv_ctrl)
{
ext_nv_manage *nv_head1 = (ext_nv_manage *)pdata1;
ext_nv_manage *nv_head2 = (ext_nv_manage *)pdata2;
ext_nv_manage *nv_head = TD_NULL;
if (nv_head1->seq > nv_head2->seq) {
nv_ctrl->current_addr = flash_addr;
nv_ctrl->next_addr = flash_addr + HNV_BLOCK_SIZE;
nv_head = nv_head1;
} else {
nv_ctrl->current_addr = flash_addr + HNV_BLOCK_SIZE;
nv_ctrl->next_addr = flash_addr;
nv_head = nv_head2;
}
nv_ctrl->total_num = nv_head->total_num;
nv_ctrl->seq = nv_head->seq;
nv_ctrl->index = (ext_nv_item_index *)&nv_head->nv_item_data[0];
}
EXT_PRV td_u32 nv_init_data_check_all_fail(td_u32 work_addr, td_u32 source_file_addr, ext_nv_type nv_type,
ext_nv_ctrl *nv_ctrl, td_u8 *pdata1)
{
td_u32 ret;
td_u32 flash_addr = work_addr;
ext_nv_manage *nv_head = TD_NULL;
ext_nv_manage *nv_head1 = (ext_nv_manage *)pdata1;
if (nv_type != EXT_TYPE_FACTORY_NV) {
ret = nv_repair_from_backup(pdata1, work_addr, source_file_addr);
if (ret != EXT_ERR_SUCCESS) {
return ret;
}
nv_ctrl->current_addr = flash_addr;
nv_ctrl->next_addr = flash_addr + HNV_BLOCK_SIZE;
nv_head = nv_head1;
nv_ctrl->total_num = nv_head->total_num; /* to be optimized : too much repetition */
nv_ctrl->seq = nv_head->seq;
nv_ctrl->index = (ext_nv_item_index *)&nv_head->nv_item_data[0];
} else {
/* factory nv unsupport repair */
ret = EXT_ERR_NOT_SUPPORT;
return ret;
}
return ret;
}
EXT_PRV td_u32 nv_init_data_check_once_fail(td_u8 *pdata1, td_u32 check1, td_u8 *pdata2, td_u32 flash_addr,
ext_nv_ctrl *nv_ctrl)
{
td_u32 ret;
td_u32 temp;
td_u8 *to_repair_data = TD_NULL;
td_u8 *right_data = TD_NULL;
ext_nv_manage *nv_head = TD_NULL;
ext_nv_manage *nv_head1 = (ext_nv_manage *)pdata1;
ext_nv_manage *nv_head2 = (ext_nv_manage *)pdata2;
if (check1 != EXT_ERR_SUCCESS) {
nv_ctrl->current_addr = flash_addr + HNV_BLOCK_SIZE;
nv_ctrl->next_addr = flash_addr;
right_data = pdata2;
to_repair_data = pdata1;
nv_head = nv_head2;
} else {
nv_ctrl->current_addr = flash_addr;
nv_ctrl->next_addr = flash_addr + HNV_BLOCK_SIZE;
right_data = pdata1;
to_repair_data = pdata2;
nv_head = nv_head1;
}
nv_ctrl->total_num = nv_head->total_num;
nv_ctrl->seq = nv_head->seq;
nv_ctrl->index = (ext_nv_item_index *)&nv_head->nv_item_data[0];
/* stage of data repair */
nv_ctrl->seq += 1;
nv_repair_file(to_repair_data, right_data, HNV_BLOCK_SIZE, nv_ctrl->seq);
ret = uapi_flash_erase(nv_ctrl->next_addr, HNV_BLOCK_SIZE);
if (ret != EXT_ERR_SUCCESS) {
return ret;
}
ret = uapi_flash_write(nv_ctrl->next_addr, HNV_BLOCK_SIZE, to_repair_data, TD_FALSE);
if (ret != EXT_ERR_SUCCESS) {
return ret;
}
temp = nv_ctrl->current_addr;
nv_ctrl->current_addr = nv_ctrl->next_addr;
nv_ctrl->next_addr = temp;
return ret;
}
/* stage of data check */
EXT_PRV td_u32 nv_init_data_check(td_u32 work_addr, td_u32 source_file_addr, ext_nv_type nv_type, ext_nv_ctrl *nv_ctrl,
td_u8 *data[2]) /* length 2 */
{
td_u32 check1, check2;
td_u32 ret = EXT_ERR_SUCCESS;
td_u32 flash_addr = work_addr;
if ((data[0] == TD_NULL) || (data[1] == TD_NULL)) {
return EXT_ERR_FAILURE;
}
check1 = nv_check_file(data[0], HNV_BLOCK_SIZE, nv_type);
check2 = nv_check_file(data[1], HNV_BLOCK_SIZE, nv_type);
if ((check1 == EXT_ERR_SUCCESS) && (check2 == EXT_ERR_SUCCESS)) {
nv_init_data_check_all_success(data[0], data[1], flash_addr, nv_ctrl);
} else if ((check1 != EXT_ERR_SUCCESS) && (check2 != EXT_ERR_SUCCESS)) {
ret = nv_init_data_check_all_fail(work_addr, source_file_addr, nv_type, nv_ctrl, data[0]);
} else {
ret = nv_init_data_check_once_fail(data[0], check1, data[1], flash_addr, nv_ctrl);
}
return ret;
}
EXT_PRV td_u32 nv_init_index_space(ext_nv_ctrl *nv_ctrl)
{
td_u32 ret = EXT_ERR_SUCCESS;
td_u32 index_size;
td_u8 *index = TD_NULL;
if (uint_2_multiply((td_u32)sizeof(ext_nv_item_index), (td_u32)nv_ctrl->total_num, &index_size) == TD_FALSE) {
return EXT_ERR_FAILURE;
}
index = boot_malloc(index_size);
if (index == TD_NULL) {
ret = EXT_ERR_MALLOC_FAILUE;
return ret;
}
if (memcpy_s(index, index_size, nv_ctrl->index,
sizeof(ext_nv_item_index) * nv_ctrl->total_num) != EXT_ERR_SUCCESS) {
nv_print_f0("memcpy_s fail.");
}
nv_ctrl->index = (ext_nv_item_index *)index;
nv_ctrl->init_flag = TD_TRUE;
return ret;
}
td_u32 nv_init_common(td_u32 work_addr, td_u32 source_file_addr, ext_nv_type nv_type)
{
td_u32 ret;
ext_nv_ctrl *nv_ctrl = &g_nv_ctrl[nv_type];
td_u8 *pdata1 = TD_NULL;
td_u8 *pdata2 = TD_NULL;
td_u32 flash_addr = work_addr;
if (nv_ctrl->init_flag == TD_TRUE) {
ret = EXT_ERR_INIT_ALREADY;
return ret;
}
/* stage of data prepare */
ret = nv_init_data_prepare(&pdata1, flash_addr);
if (ret != EXT_ERR_SUCCESS) {
if (pdata1 != TD_NULL) {
boot_free(pdata1);
}
return ret;
}
ret = nv_init_data_prepare(&pdata2, flash_addr + HNV_BLOCK_SIZE);
if (ret != EXT_ERR_SUCCESS) {
boot_free(pdata1);
if (pdata2 != TD_NULL) {
boot_free(pdata2);
}
return ret;
}
do {
/* stage of data check */
td_u8 *data[2] = { pdata1, pdata2 }; /* array length 2 */
ret = nv_init_data_check(work_addr, source_file_addr, nv_type, nv_ctrl, data);
if (ret != EXT_ERR_SUCCESS) {
break;
}
if (nv_ctrl->total_num > NV_TOTAL_NUM_MAX) {
break;
}
/* apply for index space */
ret = nv_init_index_space(nv_ctrl);
} while (0);
/* original backup not repair temporarily */
boot_free(pdata2);
boot_free(pdata1);
return ret;
}
td_u32 nv_read_common(td_u8 id, td_pvoid pdata, td_u8 len, ext_nv_type nv_type)
{
td_u32 ret;
td_u32 i;
td_u32 crc = 0;
td_u8 item_len;
td_bool to_check_crc;
/* check parameter */
if ((pdata == TD_NULL) || (len == 0)) {
return EXT_ERR_INVALID_PARAMETER;
}
ext_nv_ctrl *nv_ctrl = &g_nv_ctrl[nv_type];
if (nv_ctrl->init_flag != TD_TRUE) {
return EXT_ERR_NO_INIT;
}
ext_nv_item_index *nv_index = nv_find_item(TD_NULL, id, nv_type);
if (nv_index == TD_NULL) {
nv_print_f2("can't find nv id 0x%x len=%x\r\n", id, len);
return EXT_ERR_NOT_FOUND;
}
if (nv_index->nv_len == len) {
item_len = len;
to_check_crc = TD_TRUE;
} else {
item_len = uapi_min(nv_index->nv_len, len);
to_check_crc = TD_FALSE;
}
/* allow error */
for (i = 0; i < HNV_FAULT_TOLERANT_TIMES; i++) {
ret = uapi_flash_read(nv_ctrl->current_addr + nv_index->nv_offset, item_len, pdata);
if (ret != EXT_ERR_SUCCESS) {
continue;
}
/* check crc when length is the same */
if (to_check_crc == TD_TRUE) {
ret = uapi_flash_read(nv_ctrl->current_addr + nv_index->nv_offset + item_len, sizeof(crc), (td_u8 *)&crc);
if (ret != EXT_ERR_SUCCESS) {
continue;
}
if (crc != uapi_crc32(0, pdata, item_len)) {
continue;
}
}
break;
}
if (i >= HNV_FAULT_TOLERANT_TIMES) {
ret = EXT_ERR_FAIL_N_TIMES;
}
return ret;
}
EXT_PRV td_u32 nv_write_para_check(const td_pvoid pdata, td_u8 len, ext_nv_type nv_type, ext_nv_ctrl **nv_ctrl)
{
if ((pdata == TD_NULL) || (len == 0) || (len > HNV_ITEM_MAXLEN)) {
return EXT_ERR_INVALID_PARAMETER;
}
*nv_ctrl = &g_nv_ctrl[nv_type];
if ((*nv_ctrl)->init_flag != TD_TRUE) {
return EXT_ERR_NO_INIT;
}
return EXT_ERR_SUCCESS;
}
EXT_PRV td_u32 nv_write_cmp_readback(td_u8 id, const td_pvoid pdata, td_u8 len, ext_nv_type nv_type)
{
td_u32 ret;
td_u8 *item_readback = TD_NULL;
item_readback = boot_malloc(HNV_ITEM_MAXLEN);
if (item_readback == TD_NULL) {
return EXT_ERR_MALLOC_FAILUE;
}
ret = nv_read_common(id, item_readback, len, nv_type);
if (ret != EXT_ERR_SUCCESS) {
boot_free(item_readback);
return ret;
}
if (memcmp(pdata, item_readback, len) == 0) {
boot_free(item_readback);
return EXT_ERR_SUCCESS;
}
boot_free(item_readback);
return EXT_ERR_NOT_FOUND;
}
typedef struct {
td_pvoid pdata;
td_u8 len;
td_u8 pad[3]; /* pad3 */
} nv_write_data_and_len;
EXT_PRV td_u32 nv_write_update_data(const nv_write_data_and_len *data_and_len, const ext_nv_ctrl *nv_ctrl,
td_u8 *nv_file, const ext_nv_item_index *nv_index, ext_nv_manage **out_head)
{
td_u32 ret;
td_u32 crc;
td_u8 item_len = data_and_len->len;
ext_nv_manage *nv_head = TD_NULL;
if (out_head == TD_NULL) {
return EXT_ERR_FAILURE;
}
ret = uapi_flash_read(nv_ctrl->current_addr, HNV_BLOCK_SIZE, nv_file);
if (ret != EXT_ERR_SUCCESS) {
return ret;
}
/* update data includes 2 of crc and seq */
crc = uapi_crc32(0, data_and_len->pdata, item_len);
if (item_len > 0 && memcpy_s(nv_file + nv_index->nv_offset, HNV_BLOCK_SIZE - nv_index->nv_offset,
data_and_len->pdata, item_len) != 0) {
return EXT_ERR_FAILURE;
}
if (memcpy_s(nv_file + nv_index->nv_offset + item_len, HNV_BLOCK_SIZE - nv_index->nv_offset - item_len, &crc,
sizeof(crc)) != 0) {
return EXT_ERR_FAILURE;
}
nv_head = (ext_nv_manage *)nv_file;
nv_head->seq += 1;
nv_head->crc = uapi_crc32(0, nv_file + 8, HNV_BLOCK_SIZE - 8); /* shifts 8 */
ret = uapi_flash_erase(nv_ctrl->next_addr, HNV_BLOCK_SIZE);
ret += uapi_flash_write(nv_ctrl->next_addr, HNV_BLOCK_SIZE, nv_file, TD_FALSE);
if (ret != EXT_ERR_SUCCESS) {
return EXT_ERR_FAILURE;
}
*out_head = nv_head;
return EXT_ERR_SUCCESS;
}
td_u32 nv_write_common(td_u8 id, const td_pvoid pdata, td_u8 len, ext_nv_type nv_type)
{
ext_nv_ctrl *nv_ctrl = TD_NULL;
ext_nv_manage *nv_head = TD_NULL;
td_u8 i;
/* backup mutually and write for 2 copies when operate factory part */
td_u8 backup_num = (nv_type == EXT_TYPE_FACTORY_NV) ? 2 : 1; /* 2 */
/* check parameter */
td_u32 ret = nv_write_para_check(pdata, len, nv_type, &nv_ctrl);
if (ret != EXT_ERR_SUCCESS) {
return ret;
}
/* not write when read back is the same */
ret = nv_write_cmp_readback(id, pdata, len, nv_type);
if (ret != EXT_ERR_NOT_FOUND) {
return ret;
}
/* formally write process */
ext_nv_item_index *nv_index = nv_find_item(TD_NULL, id, nv_type);
if (nv_index == TD_NULL) {
return EXT_ERR_NOT_FOUND;
}
nv_write_data_and_len data_and_len;
data_and_len.pdata = pdata;
/* length handle */
data_and_len.len = len;
if (nv_index->nv_len != len) {
data_and_len.len = uapi_min(nv_index->nv_len, len);
}
/* read 4K for replace, and calc crc again */
td_u8 *nv_file = boot_malloc(HNV_BLOCK_SIZE);
for (; backup_num > 0; backup_num--) {
for (i = 0; i < HNV_FAULT_TOLERANT_TIMES; i++) {
ret = nv_write_update_data(&data_and_len, nv_ctrl, nv_file, nv_index, &nv_head);
if (ret != EXT_ERR_SUCCESS) {
continue;
}
break;
}
/* refresh global info */
td_u32 temp = nv_ctrl->current_addr;
nv_ctrl->current_addr = nv_ctrl->next_addr;
nv_ctrl->next_addr = temp;
if (nv_head != TD_NULL) {
nv_ctrl->seq = nv_head->seq;
nv_head = TD_NULL;
}
if (i >= HNV_FAULT_TOLERANT_TIMES) {
boot_free(nv_file);
return EXT_ERR_FAIL_N_TIMES;
}
}
boot_free(nv_file);
return EXT_ERR_SUCCESS;
}
td_u32 uapi_factory_nv_init(td_u32 flash_addr)
{
return nv_init_common(flash_addr, 0, EXT_TYPE_FACTORY_NV);
}
td_u32 uapi_factory_nv_read(td_u8 id, td_pvoid pdata, td_u8 len)
{
return nv_read_common(id, pdata, len, EXT_TYPE_FACTORY_NV);
}
td_u32 uapi_factory_nv_write(td_u8 id, const td_pvoid pdata, td_u8 len)
{
return nv_write_common(id, pdata, len, EXT_TYPE_FACTORY_NV);
}
td_u32 uapi_temp_nv_init(td_u32 flash_addr, td_u32 backup_addr)
{
return nv_init_common(flash_addr, backup_addr, EXT_TYPE_TEMP);
}
td_u32 uapi_common_nv_read(td_u8 id, td_pvoid pdata, td_u8 len, ext_nv_type nv_type)
{
return nv_read_common(id, pdata, len, nv_type);
}
td_u32 uapi_common_nv_write(td_u8 id, EXT_CONST td_pvoid pdata, td_u8 len, ext_nv_type nv_type)
{
return nv_write_common(id, pdata, len, nv_type);
}
/* fallowing functions are nv interfaces */
td_u32 uapi_nv_check_file(td_u32 flash_addr)
{
td_u32 ret;
td_u8 *data = NULL;
ext_nv_manage nv_head = { 0 };
ret = uapi_flash_read(flash_addr, sizeof(ext_nv_manage), (td_u8 *)&nv_head);
if (ret != EXT_ERR_SUCCESS) {
return ret;
}
data = boot_malloc(nv_head.flash_size);
if (data == TD_NULL) {
return EXT_ERR_MALLOC_FAILUE;
}
ret = uapi_flash_read(flash_addr, nv_head.flash_size, data);
if (ret != EXT_ERR_SUCCESS) {
boot_free(data);
return ret;
}
ret = nv_check_file(data, nv_head.flash_size, EXT_TYPE_NV);
if (ret != EXT_ERR_SUCCESS) {
boot_free(data);
return ret;
}
boot_free(data);
return EXT_ERR_SUCCESS;
}
td_u32 uapi_nv_get_ver(td_u32 flash_addr, td_u32 *ver)
{
td_u32 ret;
if (ver == TD_NULL) {
return EXT_ERR_INVALID_PARAMETER;
}
ret = uapi_flash_read(flash_addr + ext_struct_offset(ext_nv_manage, ver_magic), sizeof(td_u32), (td_u8 *)ver);
if (ret != EXT_ERR_SUCCESS) {
return EXT_ERR_FAILURE;
}
return EXT_ERR_SUCCESS;
}