/* * Copyright (c) CompanyNameMagicTag 2012-2019. All rights reserved. * Description: All code updates to get address information from partition table, * B4 can compile flash_partition_table.c code * Author: CompanyName * Create: 2012-08-28 */ #include "flash_partition_table.h" #include #include #include #include #include #include #include #include #include #include #define partition_id(i) (i) /* Corresponding partition table, input as subscript, output as id */ #define array_index(id) (id) /* Corresponding partition table, input is id, output is subscript */ #define FACTORY_NV_ADDR_REG (DW21_SYSCTRL_SC_GEN_REG3_REG) /* Low 24bit indicates factory NV address */ #define FACTORY_NV_ADDR_MASK 0xFFFFFF #define REFRESH_IDLE 0 #define REFRESH_BUSY 1 #define BLOCK_SIZE PRODUCT_CFG_FLASH_BLOCK_SIZE #define PT_DEBUG_PRINT printf /* Codex rectification, need to judge the range of NV address value */ #define NV_ADDR_VALUE_MAX (16 * 1024 * 1024) static ext_flash_partition_table g_partition_table; static td_u32 g_partition_sem_handle; __isr ext_flash_partition_table *uapi_get_partition_table(td_void) { return &g_partition_table; } /* Internal interface */ td_u32 flash_partition_get_ver(ext_partition_ver_info *ver) { td_u32 ret; ext_flash_partition_table *ptable = TD_NULL; /* The internal interface does not judge the legality of the entry. */ ptable = uapi_malloc(EXT_MOD_ID_PARTITION, sizeof(ext_flash_partition_table)); if (ptable == TD_NULL) { return EXT_ERR_PARTITION_MALLOC_FAIL; } ret = nv_read_common(EXT_NV_FTM_FLASH_PARTIRION_TABLE_ID, ptable, sizeof(ext_flash_partition_table), EXT_TYPE_FACTORY_NV); if (ret == EXT_ERR_SUCCESS) { if (memcpy_s((td_pvoid)ver, sizeof(ext_partition_ver_info), (td_pvoid)&ptable->ver, sizeof(ptable->ver)) != EOK) { uapi_free(EXT_MOD_ID_PARTITION, ptable); return EXT_ERR_FAILURE; } } uapi_free(EXT_MOD_ID_PARTITION, ptable); return ret; } td_u32 uapi_flash_partition_get_ver(ext_partition_ver_info *ver) { if (ver == TD_NULL) { return EXT_ERR_PARTITION_INVALID_PARAM; } return flash_partition_get_ver(ver); } EXT_PRV ext_partition_ver flash_partition_check_ver(EXT_CONST ext_partition_ver_info *ver) { ext_partition_ver_info local_ver; td_u32 ret; /* Incompatible with the old version, this restriction is only done under the kernel, not in the boot */ ret = flash_partition_get_ver(&local_ver); /* The local version number is read from 0x4 */ if (ret != EXT_ERR_SUCCESS) { return EXT_PARTITION_VER_ERR; } if (local_ver.ver_num == 0) { return EXT_PARTITION_VER_ERR; } if ((local_ver.bitmap & ver->bitmap) == 0) { return EXT_PARTITION_VER_ERR; } if (local_ver.ver_num > ver->ver_num) { return EXT_PARTITION_VER_ERR; } else if (local_ver.ver_num == ver->ver_num) { return EXT_PARTITION_VER_THE_SAME; } else { return EXT_PARTITION_VER_NEW; } } ext_partition_ver uapi_flash_partition_check_ver(EXT_CONST ext_partition_ver_info *ver) { if (ver == TD_NULL) { return EXT_PARTITION_VER_ERR; } return flash_partition_check_ver(ver); } /* ext_flash_partition_table_s+ext_pt_change_ctrl_s */ EXT_PRV td_u32 flash_partition_check(const ext_flash_partition_chage_info *change_info, td_u16 size) { td_u8 i; ext_partition_ver ver_ret; ext_flash_partition_table *old_info = uapi_get_partition_table(); /* Check rule: the length is in line with expectations, and the maximum length is not judged for expansion */ if (size < sizeof(ext_flash_partition_chage_info)) { return EXT_ERR_PARTITION_INVALID_PARAM; } if (change_info->change_ctrl_info.total_num > EXT_FLASH_PARTITON_MAX) { return EXT_ERR_PARTITION_INVALID_PARAM; } if (size < sizeof(ext_flash_partition_chage_info) + sizeof(ext_pt_change_detail) * change_info->change_ctrl_info.total_num) { return EXT_ERR_PARTITION_INVALID_PARAM; } /* Check rule: not the same as the original partition table */ if (memcmp(old_info, &change_info->new_info, sizeof(ext_flash_partition_table)) == 0) { return EXT_ERR_PARTITION_UPDATA_TBL_SAME; } /* Detect partition version number */ ver_ret = flash_partition_check_ver(&change_info->change_ctrl_info.ver_support); if (ver_ret == EXT_PARTITION_VER_THE_SAME) { return EXT_ERR_PARTITION_UPDATA_VER_SAME; } else if (ver_ret != EXT_PARTITION_VER_NEW) { return EXT_ERR_PARTITION_UPDATA_VER_ERR; } for (i = 0; i < change_info->change_ctrl_info.total_num; i++) { if (change_info->change_item[i].id <= partition_id(EXT_FLASH_PARTITON_KERNEL)) { return EXT_ERR_PARTITION_UPDATA_NOT_SUPPORT_ARER; } /* If it is smaller than the actual kernel size, it will fail */ /* If the size is smaller than the actual upgrade area, it will fail */ } return EXT_ERR_SUCCESS; } EXT_PRV td_void flash_partition_changinfo_2_changelog(const ext_flash_partition_chage_info *change_info, ext_pt_change_log *change_log) { ext_pt_change_status *change_log_items = change_log->table; td_u32 i; (td_void)memset_s(change_log, sizeof(ext_pt_change_log), 0, sizeof(ext_pt_change_log)); (td_void)memcpy_s(&change_log->ctrl, sizeof(ext_pt_change_ctrl), &change_info->change_ctrl_info, sizeof(change_info->change_ctrl_info)); for (i = 0; i < change_info->change_ctrl_info.total_num; i++) { change_log_items[i].id = change_info->change_item[i].id; change_log_items[i].top1_or_bottom0 = change_info->change_item[i].top1_or_bottom0; change_log_items[i].move = TD_TRUE; } /* Write the modification method to NV */ change_log->ctrl.status = REFRESH_BUSY; return; } td_u32 uapi_flash_partition_config(ext_flash_partition_chage_info *change_info, td_u16 size) { td_u32 ret; ext_pt_change_log *change_log = TD_NULL; if (change_info == TD_NULL) { return EXT_ERR_INVALID_PARAMETER; } change_log = uapi_malloc(EXT_MOD_ID_PARTITION, sizeof(ext_pt_change_log)); if (change_log == TD_NULL) { ret = EXT_ERR_PARTITION_MALLOC_FAIL; goto END; } /* Verify the correctness of the partition replacement information */ ret = flash_partition_check(change_info, size); if (ret != EXT_ERR_SUCCESS) { goto END; } /* Write the latest partition information to the backup area */ ret = uapi_factory_nv_write(EXT_NV_FTM_FLASH_PARTIRION_TABLE_B_ID, &(change_info->new_info), sizeof(ext_flash_partition_table)); if (ret != EXT_ERR_SUCCESS) { goto END; } /* Convert the contents of the partition upgrade file to the contents of the partition log file */ flash_partition_changinfo_2_changelog(change_info, change_log); /* Write partition log information to NV */ ret = uapi_factory_nv_write(EXT_NV_FTM_FLASH_PARTIRION_CHANGE_ID, change_log, sizeof(ext_pt_change_log)); if (ret != EXT_ERR_SUCCESS) { goto END; } ret = EXT_ERR_SUCCESS; END: if (change_log != TD_NULL) { uapi_free(EXT_MOD_ID_PARTITION, change_log); } return ret; } td_u32 uapi_flash_partition_get_sem_handle(td_u32 *handle) { td_u32 ret; static td_bool bool_init = TD_FALSE; if (handle == TD_NULL) { return EXT_ERR_INVALID_PARAMETER; } if (bool_init != TD_TRUE) { ret = uapi_sem_bcreate(&g_partition_sem_handle, "part_sem", EXT_SEM_ONE); if (ret != EXT_ERR_SUCCESS) { return EXT_ERR_FAILURE; } bool_init = TD_TRUE; } *handle = g_partition_sem_handle; return EXT_ERR_SUCCESS; } __init td_u32 flash_partition_init_tbl(td_void) { td_u32 ret; ext_flash_partition_table *table = TD_NULL; table = uapi_get_partition_table(); ret = uapi_factory_nv_read(EXT_NV_FTM_FLASH_PARTIRION_TABLE_ID, table, sizeof(ext_flash_partition_table)); if (ret != EXT_ERR_SUCCESS) { return EXT_ERR_NV_ERROR_READ; } return EXT_ERR_SUCCESS; } __init td_u32 flash_partition_init_ftnv(td_void) { td_u32 fnv_addr; UAPI_REG_READ(FACTORY_NV_ADDR_REG, fnv_addr); fnv_addr &= FACTORY_NV_ADDR_MASK; if (fnv_addr > NV_ADDR_VALUE_MAX) { return EXT_ERR_INVALID_PARAMETER; } return uapi_factory_nv_init(fnv_addr); } __init td_u32 uapi_flash_partition_init(td_void) { static td_bool b_init = TD_FALSE; td_u32 ret; if (b_init == TD_TRUE) { return EXT_ERR_SUCCESS; } /* 1. Initialize the factory area NV */ ret = flash_partition_init_ftnv(); /* ps:it's better move fnv init out of this function */ if (ret != EXT_ERR_SUCCESS) { return ret; } /* 2. Initialize tbl */ ret = flash_partition_init_tbl(); if (ret != EXT_ERR_SUCCESS) { return ret; } /* 3. Replace the partition table */ ret = uapi_flash_partition_refresh(EXT_PARTITON_SOFT_RESET); if (ret == EXT_ERR_SUCCESS) { b_init = TD_TRUE; /* ps:if false ,what to do ? */ } return ret; } /* must be mutually exclusive before and after the call */ EXT_PRV td_u32 flash_partition_reset(ext_partition_reset reset) { td_u32 ret; if (reset == EXT_PARTITON_SOFT_RESET) { ext_flash_partition_table *table = TD_NULL; table = uapi_get_partition_table(); ret = nv_read_common(EXT_NV_FTM_FLASH_PARTIRION_TABLE_ID, table, sizeof(ext_flash_partition_table), EXT_TYPE_FACTORY_NV); if (ret != EXT_ERR_SUCCESS) { return ret; } /* 1. The crash is instant, no need to refresh */ /* 2.NV area soft reset */ ret = uapi_nv_soft_reset(table->table[EXT_FLASH_PARTITON_NV_FILE].addr_blk * BLOCK_SIZE, table->table[EXT_FLASH_PARTITON_NV_BACKUP].addr_blk * BLOCK_SIZE); } else if (reset == EXT_PARTITON_HARD_RESET) { ret = EXT_ERR_SUCCESS; } else { ret = EXT_ERR_SUCCESS; } return ret; } typedef struct { ext_pt_change_log *inter_ctrl; ext_flash_partition_table *old_table; ext_flash_partition_table *new_table; td_u8 *tmp_buf; } partition_refresh_param; EXT_PRV td_u32 partition_refresh_create_res(partition_refresh_param *param) { /* Resource Preparation */ param->inter_ctrl = uapi_malloc(EXT_MOD_ID_PARTITION, sizeof(ext_pt_change_log)); param->new_table = uapi_malloc(EXT_MOD_ID_PARTITION, sizeof(ext_flash_partition_table)); param->tmp_buf = (td_u8 *)uapi_malloc(EXT_MOD_ID_PARTITION, BLOCK_SIZE); if (param->inter_ctrl == TD_NULL || param->new_table == TD_NULL || param->tmp_buf == TD_NULL) { return EXT_ERR_NOT_ENOUGH_MEMORY; } return EXT_ERR_SUCCESS; } EXT_PRV td_void partition_refresh_free_res(EXT_CONST partition_refresh_param *param) { if (param->inter_ctrl) { uapi_free(EXT_MOD_ID_PARTITION, param->inter_ctrl); } if (param->new_table) { uapi_free(EXT_MOD_ID_PARTITION, param->new_table); } if (param->tmp_buf) { uapi_free(EXT_MOD_ID_PARTITION, param->tmp_buf); } return; } EXT_PRV td_u32 partition_refresh_prepare1(EXT_CONST partition_refresh_param *param) { td_u32 ret = nv_read_common(EXT_NV_FTM_FLASH_PARTIRION_CHANGE_ID, param->inter_ctrl, sizeof(ext_pt_change_log), EXT_TYPE_FACTORY_NV); if (ret != EXT_ERR_SUCCESS) { return ret; } ret = nv_read_common(EXT_NV_FTM_FLASH_PARTIRION_TABLE_B_ID, param->new_table, sizeof(ext_flash_partition_table), EXT_TYPE_FACTORY_NV); if (ret != EXT_ERR_SUCCESS) { return ret; } return EXT_ERR_SUCCESS; } EXT_PRV td_bool partition_refresh_is_need(EXT_CONST partition_refresh_param *param) { if (param->inter_ctrl->ctrl.status != REFRESH_BUSY) { return TD_FALSE; } return TD_TRUE; } EXT_PRV td_u32 partition_refresh_prepare2(EXT_CONST partition_refresh_param *param, ext_nv_type *save_type) { td_u32 ret = EXT_ERR_SUCCESS; if (param->inter_ctrl->ctrl.temp_area_blk != 0) { *save_type = EXT_TYPE_TEMP; /* Refresh data ,ps:why? */ ret = nv_init_common(param->inter_ctrl->ctrl.temp_area_blk * BLOCK_SIZE, param->old_table->table[EXT_FLASH_PARTITON_FACTORY_NV].addr_blk * BLOCK_SIZE, *save_type); if (ret != EXT_ERR_SUCCESS) { return ret; } ret = nv_read_common(EXT_NV_FTM_FLASH_PARTIRION_CHANGE_ID, param->inter_ctrl, sizeof(ext_pt_change_log), *save_type); /* ps:reread? */ if (ret != EXT_ERR_SUCCESS) { return ret; } } else { *save_type = EXT_TYPE_FACTORY_NV; } return EXT_ERR_SUCCESS; } typedef struct { td_u32 cur_idx; td_u32 ctrl_idx; td_u32 src_addr; td_u32 dst_addr; td_u32 src_end_addr; td_u32 dst_end_addr; } partition_move_item_param; EXT_PRV td_bool partiton_refresh_is_move_item(EXT_CONST partition_refresh_param *param, partition_move_item_param *item_param) { td_u32 cur_idx = item_param->cur_idx; td_u32 ctrl_idx = item_param->ctrl_idx; td_u32 old_table_size = 0; td_u32 new_table_size = 0; if (param->inter_ctrl->table[ctrl_idx].move == TD_FALSE) { return TD_FALSE; } if (uint_2_multiply(param->old_table->table[cur_idx].addr_blk, BLOCK_SIZE, &old_table_size) == TD_FALSE || uint_2_multiply(param->new_table->table[cur_idx].addr_blk, BLOCK_SIZE, &new_table_size) == TD_FALSE) { return TD_FALSE; } item_param->src_addr = old_table_size; item_param->dst_addr = new_table_size; item_param->src_end_addr = param->old_table->table[cur_idx].addr_blk * BLOCK_SIZE + param->old_table->table[cur_idx].size_blk * BLOCK_SIZE; item_param->dst_end_addr = param->new_table->table[cur_idx].addr_blk * BLOCK_SIZE + param->new_table->table[cur_idx].size_blk * BLOCK_SIZE; if (param->new_table->table[ctrl_idx].dir == 0) { /* Does not consider the direction of the front and rear versions */ if (item_param->src_addr == item_param->dst_addr) { return TD_FALSE; } } else { if (item_param->src_end_addr == item_param->dst_end_addr) { return TD_FALSE; } } return TD_TRUE; } EXT_PRV td_u32 partition_refresh_copy_one_blk(EXT_CONST partition_refresh_param *param, td_u32 src_addr, td_u32 dst_addr) { td_u32 ret; ret = uapi_flash_read_in(src_addr, BLOCK_SIZE, param->tmp_buf); if (ret != EXT_ERR_SUCCESS) { return ret; } ret = uapi_flash_erase_in(dst_addr, BLOCK_SIZE); ret |= uapi_flash_write_in(dst_addr, BLOCK_SIZE, param->tmp_buf, TD_FALSE); if (ret != EXT_ERR_SUCCESS) { return ret; } return ret; } EXT_PRV td_bool partiton_refresh_move_item(EXT_CONST partition_refresh_param *param, partition_move_item_param *item_param, ext_nv_type save_type) { td_u32 ret = EXT_ERR_SUCCESS; td_u32 total_size; /* Actually move the size, the three compare the small value, the first two contrast size */ td_s32 size_finished; td_u32 abs_size_finished; td_u32 len; td_s32 size_per_time; td_u32 cur_idx = item_param->cur_idx; td_u32 ctrl_idx = item_param->ctrl_idx; if (uint_2_multiply(param->old_table->table[cur_idx].addr_blk, BLOCK_SIZE, TD_NULL) == TD_FALSE || uint_2_multiply(param->new_table->table[cur_idx].addr_blk, BLOCK_SIZE, TD_NULL) == TD_FALSE) { return TD_FALSE; } /* Take size small value move */ total_size = (param->old_table->table[cur_idx].size_blk <= param->new_table->table[cur_idx].size_blk) ? (param->old_table->table[cur_idx].size_blk * BLOCK_SIZE) : (param->new_table->table[cur_idx].size_blk * BLOCK_SIZE); abs_size_finished = (param->inter_ctrl->table[cur_idx].finished_block) * BLOCK_SIZE; if ((param->inter_ctrl->table[ctrl_idx].top1_or_bottom0 == EXT_PARTITION_TB_TOP) || ((item_param->dst_addr > item_param->src_addr) && (param->inter_ctrl->table[ctrl_idx].top1_or_bottom0 == EXT_PARTITION_TB_AUTO))) { /* High address moves first, address grows down */ size_per_time = 0 - BLOCK_SIZE; size_finished = (td_s32)(0 - abs_size_finished); /* Last corresponding block address */ item_param->src_addr = item_param->src_end_addr - BLOCK_SIZE; item_param->dst_addr = item_param->dst_end_addr - BLOCK_SIZE; } else { /* Low address moves first, address grows up */ size_per_time = BLOCK_SIZE; size_finished = (td_s32)abs_size_finished; } item_param->src_addr = (td_u32)((td_s32)item_param->src_addr + size_finished); /* This is not fault-tolerant */ item_param->dst_addr = (td_u32)((td_s32)item_param->dst_addr + size_finished); for (len = abs_size_finished; len < total_size;) { ret = partition_refresh_copy_one_blk(param, item_param->src_addr, item_param->dst_addr); if (ret != EXT_ERR_SUCCESS) { return (td_bool)ret; } item_param->src_addr = (td_u32)((td_s32)item_param->src_addr + size_per_time); item_param->dst_addr = (td_u32)((td_s32)item_param->dst_addr + size_per_time); len += BLOCK_SIZE; param->inter_ctrl->table[cur_idx].finished_block = len / BLOCK_SIZE; ret = nv_write_common(EXT_NV_FTM_FLASH_PARTIRION_CHANGE_ID, param->inter_ctrl, sizeof(ext_pt_change_log), save_type); if (ret != EXT_ERR_SUCCESS) { return (td_bool)ret; } } return (td_bool)ret; } /* refresh real proc */ EXT_PRV td_u32 partition_refresh_move(EXT_CONST partition_refresh_param *param, ext_nv_type save_type) { td_u32 ret = EXT_ERR_SUCCESS; partition_move_item_param item_param = { 0 }; for (td_u32 ctrl_idx = 0; ctrl_idx < param->inter_ctrl->ctrl.total_num; ctrl_idx++) { item_param.ctrl_idx = ctrl_idx; item_param.cur_idx = array_index(param->inter_ctrl->table[ctrl_idx].id); if (TD_FALSE == partiton_refresh_is_move_item(param, &item_param)) { continue; } ret = partiton_refresh_move_item(param, &item_param, save_type); if (ret != EXT_ERR_SUCCESS) { return ret; } param->inter_ctrl->table[ctrl_idx].move = TD_FALSE; ret = nv_write_common(EXT_NV_FTM_FLASH_PARTIRION_CHANGE_ID, param->inter_ctrl, sizeof(ext_pt_change_log), save_type); if (ret != EXT_ERR_SUCCESS) { return ret; } } return ret; } EXT_PRV td_u32 partition_refresh_finish(EXT_CONST partition_refresh_param *param, ext_partition_reset reset) { /* wrtite new tbl */ td_u32 ret = nv_write_common(EXT_NV_FTM_FLASH_PARTIRION_TABLE_ID, param->new_table, sizeof(ext_flash_partition_table), EXT_TYPE_FACTORY_NV); if (ret != EXT_ERR_SUCCESS) { return ret; } /* write ctrl */ param->inter_ctrl->ctrl.status = REFRESH_IDLE; ret = nv_write_common(EXT_NV_FTM_FLASH_PARTIRION_CHANGE_ID, param->inter_ctrl, sizeof(ext_pt_change_log), EXT_TYPE_FACTORY_NV); if (ret != EXT_ERR_SUCCESS) { return ret; } ret = flash_partition_reset(reset); if (ret != EXT_ERR_SUCCESS) { return ret; } return EXT_ERR_SUCCESS; } td_u32 uapi_flash_partition_refresh(ext_partition_reset reset) { td_u32 ret; ext_nv_type save_type; partition_refresh_param param = { 0 }; param.old_table = uapi_get_partition_table(); /* init resource */ ret = partition_refresh_create_res(¶m); if (ret != EXT_ERR_SUCCESS) { goto END; } /* init cfg from nv */ ret = partition_refresh_prepare1(¶m); if (ret != EXT_ERR_SUCCESS) { goto END; } if (TD_FALSE == partition_refresh_is_need(¶m)) { ret = EXT_ERR_SUCCESS; /* Does not require a refresh to return directly to success */ goto END; } /* ps:??????? */ ret = partition_refresh_prepare2(¶m, &save_type); if (ret != EXT_ERR_SUCCESS) { goto END; } /* do refresh */ partition_refresh_move(¶m, save_type); if (ret != EXT_ERR_SUCCESS) { goto END; } /* finish work */ ret = partition_refresh_finish(¶m, reset); END: partition_refresh_free_res(¶m); return ret; }