/** * @file dloader.c * * Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. * * Description: device downloader program * * Revision History: * ----------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dloader.h" #ifdef CONFIG_DLOADER_WITH_USB #include "dloader_usb_fastboot.h" #endif #if CONFIG_HYPERBUS_MODE #define FLASH_TYPE "hyperflash" #else #define FLASH_TYPE "norflash" #endif #define VERSION_NUMBER "M00.03.00" #if (defined PART_ID) && (defined FLASH_TYPE) && (defined VERSION_NUMBER) __USED const char version_info[] = "dloader_version:" PART_ID " " FLASH_TYPE " " VERSION_NUMBER; #endif #if defined VERSION_NUMBER #define DLOADER_VERSION VERSION_NUMBER #else #define DLOADER_VERSION "M00.00.00" #endif #ifndef MAX_RSP_SIZE #define MAX_RSP_SIZE 64 #endif #define SFS_PT_NAME "sfs" #define RFD_PT_NAME "rfd" #define PARTITION_TABLE_NAME "partition" #define ALL_DISK_NAME "all" #define ADD_DIR_FUZZY_NAME "0x" #define OSPI1_STORAGE_NAME "ospi1" #define OSPI2_STORAGE_NAME "ospi2" #define EMMC1_STORAGE_NAME "emmc1" #define EMMC2_STORAGE_NAME "emmc2" #define SD1_STORAGE_NAME "sd3" #define SD2_STORAGE_NAME "sd4" #define SD_SPL_OFFSET (0x5000) #ifndef RFD_OFFSET #define RFD_OFFSET (0x100 - 16) #endif #ifndef RFD_SIZE #define RFD_SIZE (272) #endif #define SUB_PARTITION (1) #define PRI_PARTITION (0) #define PTB_NEED_FLASH (0) #define PTB_NO_NEED_FLASH (1) #define PTB_CHECK_ERROR (2) #define INVALID_PTB_OFFSET (~0llu) #define DISABLE_GPT_PTB_CHK (1) #define NOR_FLASH_PTB_SECTOR_INDEX (2) #define PTACH_SFS_WHEN_SFS_DOWNLOADING (0) #define PTACH_SFS_CRC_WHEN_SFS_DOWNLOADING (1) #define DL_ALIGN_SIZE (1024) #define SFS_RFD_RFU_SIZE (0x200) #define HYPER_FLASH_MODE_FUSE_INDEX (181) #define HYPER_FLASH_MODE_FUSE_VAL (0x00100000) #define UINT32_HEX_STR_LEN (sizeof(uint32_t) * 2) #define FUSE_INDEX_MAX (255) #define STORAGE_INIT_RESULT_FAIL (1) #ifdef CONFIG_DLOADER_WITH_USB static fastboot_t *fb_data; #endif #if CONFIG_DLOADER_FLASH extern uint8_t flash1_init_result; #endif #if CONFIG_DLOADER_EMMC extern uint8_t emmc1_init_result; #endif #if CONFIG_DLOADER_SD extern uint8_t sd1_init_result; #endif /* globol var */ void *dl_scratch_base = NULL; uint32_t dl_scratch_sz = 0; void *dl_data_base = NULL; uint32_t dl_data_sz = 0; uint8_t md5_received[MD5_LEN]; #if CONFIG_DLOADER_WITH_TRACE #define ELF_STATUS_MAGIC (0x6f707273) //"oprs" #define TRACE_PROCESS_STACK_SIZE (16384) typedef struct prog_cmd_args_t { uint64_t cur_sz; uint64_t offset; uint64_t total_sz; char storage_name[8]; char name[72]; uint64_t dl_status; uint32_t elf_status; uint32_t data_base; uint32_t data_size; } __PACKED PROG_CMD_ARGS_T; typedef struct prog_statu_t { uint32_t flag; uint32_t ret_code; } __PACKED PROG_STATUS_T; PROG_STATUS_T *prog_status = NULL; PROG_CMD_ARGS_T *cmd_args = NULL; static osThreadId_t trace_process_thread = NULL; #endif #ifdef CONFIG_DLOADER_WITH_USB #include "reset_ip.h" #define ROM_CTRL_BOOT_PIN_OVERRIDE_BIT_OFFSET 0x1 #define ROM_CTRL_BOOT_PIN_OVERRIDE_ENABLE_BIT_OFFSET 0x9 extern USBD_DEV_CFG FB_USBD_DevCfg; #endif char *boot_package[BOOT_MAX] = { BOOT_PARTITION_NAME0, BOOT_PARTITION_NAME1, BOOT_PARTITION_NAME2, }; /* static globol var */ static bool do_md5_rb_check = false; static bool do_md5_rcv_check = true; static const char *err_info[] = { [ERR_UNKNOWN] = "unkown error", [ERR_FLASH1_INIT_FAIL] = "xspi1 porta cs0 flash init fail", [ERR_EMMC1_INIT_FAIL] = "emmc1 init fail", [ERR_SD1_INIT_FAIL] = "sd3 init fail", [ERR_PRI_PTB_NOT_MATCH] = "primary partition table not match", [ERR_SUB_PTB_NOT_MATCH] = "sub partition table not match", [ERR_PT_NOT_FOUND] = "partition not found", [ERR_IMAGE_TOO_LARGE] = "image too large", [ERR_IMAGE_FORMAT_ERR] = "image format error", [ERR_PRI_PTB_NOT_FLASH] = "primary partition table has not been flashed", [ERR_SUB_PTB_NOT_FLASH] = "sub partition table has not been flashed", [ERR_PT_READ_FAIL] = "partition read error", [ERR_PT_FLASH_FAIL] = "partition flash error", [ERR_PT_ERASE_FAIL] = "erase partition error", [ERR_PT_OVERLAP] = "partition size not aligned", [ERR_PT_FULL_NAME_FORMAT] = "partition full name format error", [ERR_INVALID_BLOCK_SIZE] = "invalid block size", [ERR_SPARSE_IMAGE_SIZE_TOO_LOW] = "sparse image size too low", [ERR_SPARSE_IMAGE_HEADER] = "sparse image header error", [ERR_SPARSE_IMAGE_BUFFERED] = "buffered spare image error", [ERR_SPARSE_IMAGE_CHUNK_HEADER] = "sparse image chunk header error", [ERR_SPARSE_IMAGE_CHUNK_TOO_LARGE] = "sparse image chunk too large", [ERR_SPARSE_IMAGE_CHUNK_NOT_MATCH] = "sparse image chunk size not match type", [ERR_SPARSE_IMAGE_CHUNK_UNKNOWN] = "sparse image chunk unkown", [ERR_SPARSE_IMAGE_MALLOC] = "sparse image malloc error", [ERR_SPARSE_IMAGE_DO_NOT_SUPPORT] = "do not support sparse image download", [ERR_HASH_FAIL] = "hash check fail", [ERR_EFUSE_INDEX] = "efuse index error", [ERR_EFUSE_BURN] = "burn efuse fail", [ERR_EFUSE_READ] = "read efuse fail", [ERR_SWITCH_PART] = "emmc switch part error", [ERR_CMD_ERROR] = "can not find command in cmd_table", [ERR_PT_BASE_ERROR] = "target base error, check sfs or parition", [ERR_PT_SIZE_ERROR] = "target size error, check sfs or parition", [ERR_PTNAME_NOT_EXIST] = "can't get the download partition name", [ERR_DISK_NOT_EXIST] = "can't find a matched disk for download", [ERR_DISK_OPEN_ERROR] = "open the download disk failed", [ERR_PARTITION_READ_ERROR] = "read partition table error", [HYPER_FLASH_FUSE_READ_ERROR] = "read hyper flash fuse error", [HYPER_FLASH_FUSE_WRITE_ERROR] = "write hyper flash fuse error", [HYPER_FLASH_FUSE_VAL_ERROR] = "hyper flash fuse value error", [VERIFY_SIZE_ERROR] = "verify size error", [CAN_NOT_FIND_A_DOWNLOAD_FUNCTION] = "can not find a donwload function", [CAN_NOT_FIND_A_ERASE_FUNCTION] = "can not find a verify function", [CAN_NOT_FIND_A_VERIFY_FUNCTION] = "can not find a verify function", [ERR_HASH_FAIL_FROM_PC] = "hash from pc check fail", [ERR_DL_FUSE_SIZE_CHECK_FAIL] = "fuse bin size check fail", [ERR_DL_FUSE_LENGTH_CHECK_FAIL] = "fuse item length check fail", [ERR_DL_FUSE_ACTION_TYPE_NOT_FIND] = "fuse action type not find", [ERR_DL_FUSE_CRC_CHECK_FAIL] = "fuse crc check fail", [ERR_DL_FUSE_ENC_INIT_FAIL] = "fuse encrypto init fail", [ERR_DL_FUSE_ENC_FAIL] = "fuse encrypto fail", [ERR_DL_FUSE_MALLOC_FAIL] = "fuse malloc fail", [ERR_DL_FUSE_GEN_DATA_FAIL] = "fuse gen data fail", [ERR_DL_FUSE_READBACK_VERIFY_FAIL] = "fuse readback verify fail", [ERR_GET_SFS_FORM_MSFS_FAIL] = "get matched sfs from msfs error", }; /* flash command */ DL_ERR_CODE_E flash_sparse_img(DL_STATE_T *ds, void *data, unsigned sz, uint8_t device_inst, uint64_t ptn, uint64_t size); DL_ERR_CODE_E flash_gpt_table(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E flash_ospi_sfs_areas(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E flash_ospi_rfd_areas(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E md5_read_check(DL_STATE_T *ds, uint8_t inst, void *data, unsigned sz, uint64_t read_addr); DL_ERR_CODE_E flash_normal_partition(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E flash_emmc_pack0(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E flash_emmc_pack1(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E flash_emmc_pack2(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E flash_ospi_pack0(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E flash_ospi_pack1(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E flash_ospi_pack2(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); DL_ERR_CODE_E flash_addr_direct(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data); /* erase command */ DL_ERR_CODE_E erase_gpt_table(DL_STATE_T *ds); DL_ERR_CODE_E erase_normal_partition(DL_STATE_T *ds); DL_ERR_CODE_E erase_all(DL_STATE_T *ds); DL_ERR_CODE_E erase_ospi_sfs_areas(DL_STATE_T *ds); DL_ERR_CODE_E erase_ospi_rfd_areas(DL_STATE_T *ds); DL_ERR_CODE_E erase_emmc_pack0(DL_STATE_T *ds); DL_ERR_CODE_E erase_emmc_pack1(DL_STATE_T *ds); DL_ERR_CODE_E erase_emmc_pack2(DL_STATE_T *ds); DL_ERR_CODE_E erase_ospi_pack0(DL_STATE_T *ds); DL_ERR_CODE_E erase_ospi_pack1(DL_STATE_T *ds); DL_ERR_CODE_E erase_ospi_pack2(DL_STATE_T *ds); DL_ERR_CODE_E erase_addr_direct(DL_STATE_T *ds); /* verify command */ DL_ERR_CODE_E do_verify(DL_STATE_T *ds); DL_ERR_CODE_E verify_ospi_pack0(DL_STATE_T *ds); DL_ERR_CODE_E verify_ospi_pack1(DL_STATE_T *ds); DL_ERR_CODE_E verify_ospi_pack2(DL_STATE_T *ds); DL_ERR_CODE_E verify_addr_direct(DL_STATE_T *ds); DL_ERR_CODE_E verify_emmc_pack0(DL_STATE_T *ds); DL_ERR_CODE_E verify_emmc_pack1(DL_STATE_T *ds); DL_ERR_CODE_E verify_emmc_pack2(DL_STATE_T *ds); DL_ERR_CODE_E verify_normal_partition(DL_STATE_T *ds); static bool close_disk(DL_STATE_T *ds); static bool open_disk(DL_STATE_T *ds); /* dloader command table */ static COMMAND_TABLE_T dl_cmd_table[] = { { TYPE_PT_UNKNOWN, ALLDISK, NULL, NULL, NULL, NULL, NULL, }, { TYPE_PRI_PTB, ALLDISK, flash_gpt_table, erase_gpt_table, NULL, "flash_gpt_table", "erase_gpt_table", }, { TYPE_PRI_PT, ALLDISK, flash_normal_partition, erase_normal_partition, verify_normal_partition, "flash_normal_partition", "erase_normal_partition", }, { TYPE_SUB_PTB, ALLDISK, flash_gpt_table, erase_gpt_table, NULL, "flash_gpt_table", "erase_gpt_table", }, { TYPE_SUB_PT, ALLDISK, flash_normal_partition, erase_normal_partition, verify_normal_partition, "flash_normal_partition", "erase_normal_partition", }, { TYPE_SUB_PT_WHOLE, ALLDISK, flash_normal_partition, erase_normal_partition, verify_normal_partition, "flash_normal_partition", "erase_normal_partition", }, { TYPE_ALL_PT, ALLDISK, NULL, erase_all, NULL, NULL, "erase_all", }, { TYPE_NOT_IN_GPT, ALLDISK, NULL, NULL, NULL, NULL, NULL, }, #if CONFIG_DLOADER_EMMC || CONFIG_DLOADER_SD { TYPE_BOOT_PACK0, MMC, flash_emmc_pack0, erase_emmc_pack0, verify_emmc_pack0, "flash_emmc_pack0", "erase_emmc_pack0", }, { TYPE_BOOT_PACK1, MMC, flash_emmc_pack1, erase_emmc_pack1, verify_emmc_pack1, "flash_emmc_pack1", "erase_emmc_pack1", }, { TYPE_BOOT_PACK2, MMC, flash_emmc_pack2, erase_emmc_pack2, verify_emmc_pack2, "flash_emmc_pack2", "erase_emmc_pack2", }, #endif #if CONFIG_DLOADER_FLASH { TYPE_BOOT_PACK0, NORFLASH, flash_ospi_pack0, erase_ospi_pack0, verify_ospi_pack0, "flash_ospi_pack0", "erase_ospi_pack0", }, { TYPE_BOOT_PACK1, NORFLASH, flash_ospi_pack1, erase_ospi_pack1, verify_ospi_pack1, "flash_ospi_pack1", "erase_ospi_pack1", }, { TYPE_BOOT_PACK2, NORFLASH, flash_ospi_pack2, erase_ospi_pack2, verify_ospi_pack2, "flash_ospi_pack2", "erase_ospi_pack2", }, { TYPE_SAFETY_SFS_PT, NORFLASH, flash_ospi_sfs_areas, erase_ospi_sfs_areas, NULL, "flash_ospi_sfs_areas", "erase_ospi_sfs_areas", }, { TYPE_SAFETY_RFD_PT, NORFLASH, flash_ospi_rfd_areas, erase_ospi_rfd_areas, NULL, "flash_ospi_rfd_areas", "erase_ospi_rfd_areas", }, #endif { TYPE_ADD_DIRECT, ALLDISK, flash_addr_direct, erase_addr_direct, verify_addr_direct, "flash_addr_direct", "erase_addr_direct", }, }; /* download type table */ static DOWNLOAD_TYPE_TABLE_T dl_type_table[] = { {PARTITION_TABLE_NAME, PRI_PARTITION, "primary parition table", TYPE_PRI_PTB}, {PARTITION_TABLE_NAME, SUB_PARTITION, "sub partition table", TYPE_SUB_PTB}, {ALL_DISK_NAME, PRI_PARTITION, "all disk", TYPE_ALL_PT}, {BOOT_PARTITION_NAME0, PRI_PARTITION, "boot package0", TYPE_BOOT_PACK0}, {BOOT_PARTITION_NAME1, PRI_PARTITION, "boot package1", TYPE_BOOT_PACK1}, {BOOT_PARTITION_NAME2, PRI_PARTITION, "boot package2", TYPE_BOOT_PACK2}, #if CONFIG_DLOADER_FLASH {SFS_PT_NAME, PRI_PARTITION, "sfs", TYPE_SAFETY_SFS_PT}, {RFD_PT_NAME, PRI_PARTITION, "rfd", TYPE_SAFETY_RFD_PT}, #endif }; /* disk device for dloader, will be init in open_disk function */ #if CONFIG_DLOADER_FLASH static struct disk_dev disk_dev_norflash0 = {0}; #endif #if CONFIG_DLOADER_EMMC static struct disk_dev disk_dev_emmc0 = {0}; static struct disk_dev disk_dev_emmc0_boot1 = {0}; static struct disk_dev disk_dev_emmc0_boot2 = {0}; #endif #if CONFIG_DLOADER_SD static struct disk_dev disk_dev_sdcard0 = {0}; #endif /* all download disk state is listed here*/ static DL_STATE_T dl_state_table[] = { #if CONFIG_DLOADER_FLASH /* Nor Flash 0 */ { .disk_type = NORFLASH, .download_name = OSPI1_STORAGE_NAME, #if CONFIG_DLODER_USE_RAM_AS_DISK .disk_name = DISK_RAM_NAME(0), #else .disk_name = DISK_NOR_FLASH_NAME(0), #endif .disk_inst[0] = &disk_dev_norflash0, .disk_inst[1] = NULL, .disk_inst[2] = NULL, .ptb_offset = 0x2000, .boot_offset = 0x7000, }, #endif #if CONFIG_DLOADER_EMMC /* mmc0 */ { .disk_type = MMC, .download_name = EMMC1_STORAGE_NAME, #if CONFIG_DLODER_USE_RAM_AS_DISK .disk_name = DISK_RAM_NAME(1), .disk_boot1_name = DISK_RAM_NAME(2), .disk_boot2_name = DISK_RAM_NAME(3), #else .disk_name = DISK_MMC_NAME(0), .disk_boot1_name = DISK_MMC_BOOT_NAME(0, 1), .disk_boot2_name = DISK_MMC_BOOT_NAME(0, 2), #endif .disk_inst[0] = &disk_dev_emmc0, .disk_inst[1] = &disk_dev_emmc0_boot1, .disk_inst[2] = &disk_dev_emmc0_boot2, .ptb_offset = 0, .boot_offset = 0, }, #endif #if CONFIG_DLOADER_SD /* SD CARD0 */ { .disk_type = MMC, .download_name = SD1_STORAGE_NAME, #if CONFIG_DLODER_USE_RAM_AS_DISK .disk_name = DISK_RAM_NAME(0), #else .disk_name = DISK_MMC_NAME(1), #endif .disk_inst[0] = &disk_dev_sdcard0, .disk_inst[1] = NULL, .disk_inst[2] = NULL, .ptb_offset = 0, .boot_offset = SD_SPL_OFFSET, }, #endif }; static void dloader_hexdump(const void *ptr, size_t len) { addr_t address = (addr_t)ptr; size_t count; for (count = 0; count < len; count += 16) { union { uint32_t buf[4]; uint8_t cbuf[16]; } u; size_t s = ROUNDUP(MIN(len - count, 16), 4); size_t i; printf("0x%08x: ", address); for (i = 0; i < s / 4; i++) { u.buf[i] = ((const uint32_t *)address)[i]; printf("%08x ", u.buf[i]); } for (; i < 4; i++) { printf(" "); } printf("|"); for (i = 0; i < 16; i++) { unsigned char c = u.cbuf[i]; if (i < s && isprint(c)) { printf("%c", c); } else { printf("."); } } printf("|\r\n"); address += 16; } } /** * @brief get the lowest common multiple for "size" and "aligned" * @param size iput number * @param aligned aligned number * @return uint64_t lowest common multiple */ static uint64_t round_up(uint64_t size, uint64_t aligned) { uint64_t mod = 0; if (aligned == 0 || size < aligned) return aligned; /* Sometimes, 'aligned' is not equal to power of 2 */ mod = size % aligned; size += mod ? aligned - mod : 0; return size; } /** * @brief get the greatest common divisor for "size" and "aligned" * @param size iput number * @param aligned aligned number * @return uint64_t the greatest common divisor */ static uint64_t round_down(uint64_t size, uint64_t aligned) { uint64_t mod = 0; if (aligned == 0 || size < aligned) return 0; /* Sometimes, 'aligned' is not equal to power of 2 */ mod = size % aligned; size -= mod; return size; } /** * @brief char to hex * @param c char * @return uint8_t hex value */ static uint8_t char2hex(char c) { if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'a' && c <= 'f') { return c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { return c - 'A' + 10; } return 0; } /** * @brief string to hex * @param str * @param str_len * @param hex * @param hex_len */ static void str2hex(const char *str, uint32_t str_len, uint8_t *hex, uint32_t hex_len) { for (uint32_t i = 0; i < str_len / 2 && i < hex_len; i++) { hex[i] = 0; hex[i] = (char2hex(str[i * 2]) & 0xF) << 4; hex[i] |= char2hex(str[i * 2 + 1]) & 0xF; } } /** * @brief read sfs of nor flash * @param ds download disk states * @return int */ void read_sfs(DL_STATE_T *ds) { uint8_t *buffer = (uint8_t *)dl_scratch_base; uint32_t sfs_size = 0; struct sfs *sfs = NULL; if (ds->disk_type != NORFLASH) { ERROR("disk type must be flash type\r\n"); return; } /* sfs must have an exclusive sector */ sfs_size = round_up(SFS_SIZE, ds->block_size); sfs = (struct sfs *)pvPortMallocAligned(sfs_size, ds->block_size); if (!sfs) { ERROR("allocate memory fail\r\n"); return; } memset(buffer, 0x0, sfs_size); if (dl_disk_read(ds->disk_inst[0], 0, buffer, sfs_size)) { ERROR("read nor flash fail\r\n"); goto end; } DBG("sfs dump\r\n"); dloader_hexdump(buffer, ds->block_size); if (get_sfs_info(sfs, buffer, ds->block_size)) { ERROR("sfs error\r\n"); goto end; } DBG("sfs tag = 0x%08x\r\n", sfs->tag); DBG("sfs freq = 0x%02x\r\n", sfs->freq); DBG("sfs sw_reset_info = 0x%02x\r\n", sfs->sw_reset_info); DBG("sfs normal_img_base = 0x%08x\r\n", sfs->normal_img_base); DBG("sfs backup_img_base = 0x%08x\r\n", sfs->backup_img_base); DBG("sfs third_img_base = 0x%08x\r\n", sfs->third_img_base); DBG("sfs crc32 = 0x%08x\r\n", sfs->crc32); end: if (sfs) vPortFree(sfs); return; } /** * @brief read rfd of nor flash * @param ds download disk states * @return int */ void read_rfd(DL_STATE_T *ds) { uint8_t *buffer = (uint8_t *)dl_scratch_base; uint32_t read_size = SFS_RFD_RFU_SIZE; int i = 0; uint32_t crc32_val; if (ds->disk_type != NORFLASH) { ERROR("disk type must be flash type\n"); return; } read_size = round_up(read_size, ds->block_size); memset(buffer, 0x0, read_size); if (dl_disk_read(ds->disk_inst[0], 0, buffer, read_size)) { ERROR("read nor flash fail\n"); goto end; } DBG("rfd dump\n"); dloader_hexdump(buffer + RFD_OFFSET, RFD_SIZE); for (i = 0; i < 4; i++) { crc32_val = sfs_crc32(0, (uint8_t *)(buffer + RFD_OFFSET + 16 + i * 64), 64); DBG("crc32_val=0x%08x\n", crc32_val); } end: return; } /** * @brief read sfs and get the image base * @param ds download disk states * @param num package number * @return int */ uint32_t sfs_get_image_base(DL_STATE_T *ds, uint8_t num) { uint8_t *buffer = (uint8_t *)dl_scratch_base; uint32_t sfs_size = 0; struct sfs *sfs = NULL; uint32_t img_base = 0; if (ds->disk_type != NORFLASH) { ERROR("ds->disk_type != NORFLASH\r\n"); return 0; } /* sfs must have an exclusive sector */ sfs_size = round_up(SFS_SIZE, ds->block_size); sfs = (struct sfs *)pvPortMallocAligned(sfs_size, ds->block_size); if (!sfs) { ERROR("allocate memory fail\r\n"); return 0; } memset(buffer, 0x0, sfs_size); if (dl_disk_read(ds->disk_inst[0], 0, buffer, sfs_size)) { ERROR("read nor flash fail\r\n"); goto end; } if (get_sfs_info(sfs, buffer, ds->block_size)) { ERROR("sfs error\r\n"); goto end; } if (BOOT0_NUM == num) { DBG("get sfs normal_img_base = 0x%08x\r\n", sfs->normal_img_base); img_base = sfs->normal_img_base; } else if (BOOT1_NUM == num) { DBG("get sfs backup_img_base = 0x%08x\r\n", sfs->backup_img_base); img_base = sfs->backup_img_base; } else if (BOOT2_NUM == num) { DBG("get sfs third_img_base = 0x%08x\r\n", sfs->third_img_base); img_base = sfs->third_img_base; } else { ERROR("num = %d, must < 3\r\n", num); goto end; } end: if (sfs) vPortFree(sfs); return img_base; } /** * @brief read sfs and get the image limit size * @param ds download disk states * @param num inst number * @return int */ uint32_t sfs_get_image_size_limit(DL_STATE_T *ds, uint8_t num) { uint8_t *buffer = (uint8_t *)dl_scratch_base; uint32_t sfs_size = 0; struct sfs *sfs = NULL; uint32_t img_size = 0; uint32_t img_base[BOOT_MAX] = {0}; uint32_t max_img_base = 0; uint32_t min_img_base = 0; if (ds->disk_type != NORFLASH) { ERROR("ds->disk_type != NORFLASH\r\n"); return 0; } /* sfs must have an exclusive sector */ sfs_size = round_up(SFS_SIZE, ds->block_size); sfs = (struct sfs *)pvPortMallocAligned(sfs_size, ds->block_size); if (!sfs) { ERROR("allocate memory fail\r\n"); return 0; } memset(buffer, 0x0, sfs_size); if (dl_disk_read(ds->disk_inst[0], 0, buffer, sfs_size)) { ERROR("read nor flash fail\r\n"); goto end; } if (get_sfs_info(sfs, buffer, ds->block_size)) { ERROR("sfs error\r\n"); goto end; } img_base[BOOT0_NUM] = sfs->normal_img_base; img_base[BOOT1_NUM] = sfs->backup_img_base; img_base[BOOT2_NUM] = sfs->third_img_base; if (sfs->normal_img_base == sfs->backup_img_base) { img_base[BOOT0_NUM] = 0; } if (sfs->normal_img_base == sfs->third_img_base) { img_base[BOOT1_NUM] = 0; } if (sfs->backup_img_base == sfs->third_img_base) { img_base[BOOT2_NUM] = 0; } min_img_base = MIN(img_base[BOOT0_NUM], MIN(img_base[BOOT1_NUM], img_base[BOOT2_NUM])); max_img_base = MAX(img_base[BOOT0_NUM], MAX(img_base[BOOT1_NUM], img_base[BOOT2_NUM])); if (0 == img_base[num]) img_size = 0; else if (img_base[num] == min_img_base) { img_size = MIN(img_base[(num + 1) % 3], img_base[(num + 2) % 3]) - img_base[num]; } else if (img_base[num] == max_img_base) img_size = ds->capacity - img_base[num]; else img_size = MAX(img_base[(num + 1) % 3], img_base[(num + 2) % 3]) - img_base[num]; end: if (sfs) vPortFree(sfs); DBG("get image limited size = 0x%08x\r\n", img_size); return img_size; } /** * @brief get the error code of this operation * @param err * @param pname * @return char* */ char *response_error(enum dl_err_code err, const char *pname) { static char err_response[MAX_RSP_SIZE]; const char *info = "unknown error"; if (err < ERR_UNKNOWN || err >= ERR_MAX) { ERROR("unknown error code:%d\r\n", err); return NULL; } info = err_info[err]; snprintf(err_response, sizeof(err_response), "%04x:%s - %s", err, info, pname); return err_response; } /** * @brief compare gpt's entries * @param new GPT_header * @param old GPT_header * @param count partition_entry's number * @return int */ static int compare_gpt_entries(struct partition_entry *new, struct partition_entry *old, uint32_t count) { int ret = 0; for (uint32_t i = 0; i < count; i++) { ret |= strncmp((char *)new[i].name, (char *)(old[i].name), MAX_GPT_NAME_SIZE); // ret |= memcmp(new[i].type_guid, old[i].type_guid, // sizeof(new[i].type_guid)); // ret |= memcmp(new[i].unique_partition_guid, // old[i].unique_partition_guid, // sizeof(new[i].unique_partition_guid)); ret |= (new[i].first_lba != old[i].first_lba); ret |= (new[i].attribute_flag != old[i].attribute_flag); /* The last partition's last_lba may be extended to * storage capacity - gpt_header_block - partition_entries_blocks. * Skip it. * */ if (i != count - 1) { ret |= (new[i].last_lba != old[i].last_lba); } if (ret) break; } return ret; } /** * @brief compare gpt's head * @param new GPT_header * @param old GPT_header * @return int */ static int compare_gpt_header(GPT_header *new, GPT_header *old) { int ret = 0; ret |= memcmp(new->sign, old->sign, sizeof(new->sign)); ret |= memcmp(new->version, old->version, sizeof(new->version)); // ret |= memcmp(new->guid, old->guid, sizeof(new->guid)); ret |= (new->header_sz != old->header_sz); ret |= (new->current_lba != old->current_lba); // ret |= (new->backup_lba != old->backup_lba);//this field will be // re-calculated before written to emmc ret |= (new->first_usable_lba != old->first_usable_lba); // ret |= (new->last_usable_lba != old->last_usable_lba);//this field will // be re-calculated before written to emmc ret |= (new->partition_entry_lba != old->partition_entry_lba); ret |= (new->partition_entry_count != old->partition_entry_count); ret |= (new->partition_entry_sz != old->partition_entry_sz); ret |= (new->actual_entries_count != old->actual_entries_count); if (ret) { return ret; } ret |= compare_gpt_entries(new->partition_entries, old->partition_entries, new->actual_entries_count); return ret; } /** * @brief check if partition table need to be flash * @param ds download disk states * @param data data buffer * @param sz size of data * @return int */ static int check_partition_table(DL_STATE_T *ds, void *data, unsigned sz) { int index = 0; uint32_t ret = PTB_CHECK_ERROR; uint64_t offset = 0; uint64_t gpt_sz = 0; uint8_t *buffer = NULL; uint32_t block_size = 0; const char *ptname = ds->ptname; const char *sub_ptbname = ds->sub_ptbname; uint32_t blocks_for_entries = 0; struct partition_device *ptdev = ds->ptdev; GPT_header buffered_gpt_header = {0}; GPT_header storage_gpt_header = {0}; block_size = ds->block_size; if (!block_size || block_size % 4 != 0) { goto end; } blocks_for_entries = (NUM_PARTITIONS * PARTITION_ENTRY_SIZE) / block_size; /* MBR is in the first LBA. * GPT header is in the second LBA. * GPT entries are in the following blocks. * */ gpt_sz = (GPT_HEADER_BLOCKS + blocks_for_entries + 1) * block_size; if (sz < gpt_sz) { ERROR("download gpt image error"); goto end; } if (ds->disk_type == NORFLASH) { ret = gpt_partition_round( ((uint8_t *)data + block_size), (gpt_sz - block_size) * 2, block_size, ds->erase_size, ds->capacity - ds->ptb_offset); if (ret) { ERROR("patch nor flash ptb error! ret:%d\r\n", ret); ret = PTB_CHECK_ERROR; goto end; } } /* * LBA0 is for MBR, skip it */ ret = parse_gpt_table_from_buffer(((uint8_t *)data + block_size), gpt_sz - block_size, &buffered_gpt_header, block_size, false); if (ret) { ERROR("parse gpt header error"); ret = PTB_NEED_FLASH; goto end; } #if 0 // DISABLE_GPT_PTB_CHK ret = PTB_NEED_FLASH; goto end; #endif buffer = (uint8_t *)pvPortMallocAligned(round_up(gpt_sz, block_size), block_size); if (!buffer) { ERROR("mem allocate buffer error"); goto end; } /* If it is primary partition table, LBA0 is for MBR,skipping */ if (ds->partiton_type == TYPE_PRI_PTB) { offset = ds->ptb_offset; } else { index = ptdev_get_index(ptdev, sub_ptbname); if (index == INVALID_PTN) { goto end; } offset = ptdev_get_offset(ptdev, sub_ptbname); } DBG("ptb_offset:%lld\r\n", offset); if (offset % ds->block_size) { ERROR("offset is not aligned to block size"); goto end; } ret = dl_disk_read(ds->disk_inst[0], offset, (uint8_t *)buffer, round_up(gpt_sz, ds->block_size)); if (ret) { ERROR("read gpt table error:%s$%s$%s", ds->disk_name, sub_ptbname, ptname); dloader_hexdump(buffer, 2 * ds->block_size); ret = PTB_CHECK_ERROR; goto end; } ret = parse_gpt_table_from_buffer((buffer + block_size), gpt_sz - block_size, &storage_gpt_header, block_size, false); if (ret) { DBG("no available gpt in device, need flash partition table\r\n"); /* There is no correct gpt header in emmc, * so needs flash gpt image by returing PTB_NEED_FLASH * */ ret = PTB_NEED_FLASH; goto end; } ret = compare_gpt_header(&buffered_gpt_header, &storage_gpt_header); if (!ret) { ret = PTB_NO_NEED_FLASH; } else { ret = PTB_NEED_FLASH; } end: if (buffered_gpt_header.partition_entries != NULL) { vPortFree(buffered_gpt_header.partition_entries); } if (storage_gpt_header.partition_entries != NULL) { vPortFree(storage_gpt_header.partition_entries); } if (buffer) { vPortFree(buffer); } return ret; } /** * @brief erase nor flash areas * * @param ds download disk states * @param full_ptname parition name * @param img_sz parition s * @return true * @return false */ __UNUSED static bool erase_nor_flash_partition(DL_STATE_T *ds, uint64_t ptn, uint64_t size, uint64_t img_sz) { uint64_t erase_size; if (!img_sz || !ptn || !size) { ERROR("img_sz or ptn or size is 0\r\n"); return false; } erase_size = round_up(img_sz, ds->erase_size); erase_size = erase_size > size ? size : erase_size; erase_size = (size - erase_size > ds->erase_size) ? erase_size : size; if (dl_disk_erase(ds->disk_inst[0], ptn, erase_size)) { ERROR("erase storage error\r\n"); return false; } /* erase the last sector for footer */ if ((size - erase_size > ds->erase_size) && dl_disk_erase(ds->disk_inst[0], ptn + size - ds->erase_size, ds->erase_size)) { ERROR("erase storage error\r\n"); return false; } DBG("erase ptn = 0x%llx erase_size:0x%llx size:0x%llx group:0x%x\r\n", ptn, erase_size, size, ds->erase_size); return true; } /** * @brief verify data after download * * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E do_verify(DL_STATE_T *ds) { DL_ERR_CODE_E err = ERR_NONE; return err; } /** * @brief get verify size * * @param ds partition name,for example boot0:0x001C2000 * @return uint64_t size */ uint64_t get_verify_size(const char *ptname) { char *addrsuffix; uint64_t size = 0; char *token1 = NULL; if (!ptname) return 0; if (strstr(ptname, ":")) { token1 = strchr((const char *)ptname, ':'); if (token1) { size = strtoull(token1 + 1, &addrsuffix, 16); DBG("get_verify_size = 0x%llx\r\n", size); return size; } } return 0; } /** * @brief dloader verify comman prosess * @param fb fastboot hanlder * @param arg input argument * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E dl_cmd_verify(const char *arg, void *data, unsigned sz) { DL_ERR_CODE_E err = ERR_NONE; static uint32_t verify_cnt = 0; uint8_t i = 0; PARTITION_TYPE_E pttype = TYPE_PT_UNKNOWN; struct command_table *downloader = NULL; DL_ERR_CODE_E (*verify_cmd_callback)(DL_STATE_T *ds); DL_STATE_T *ds = NULL; /* get flash name */ ds = parse_partition_name(arg, &err); if (!ds || err != ERR_NONE) { goto end; } ASSERT(ds); DBG("------------------verify = %d-------------------\r\n", verify_cnt++); /* do flashing work */ for (i = 0; i < ARRAY_SIZE(dl_cmd_table); i++) { if (ds->partiton_type == dl_cmd_table[i].partiton_type) { if (ds->disk_type == dl_cmd_table[i].disk_type || dl_cmd_table[i].disk_type == ALLDISK) { downloader = &dl_cmd_table[i]; DBG("find download cmd, i = %d\r\n", i); break; } } } if (!downloader) { ERROR("can not find command in cmd_table\r\n"); err = ERR_CMD_ERROR; goto closedisk; } verify_cmd_callback = downloader->verify_cmd_callback; if (verify_cmd_callback) { err = verify_cmd_callback(ds); if (err) { ERROR("verify error\r\n"); goto closedisk; } } else { ERROR("verify_cmd_callback is NULL\r\n"); err = CAN_NOT_FIND_A_VERIFY_FUNCTION; goto closedisk; } /* If the image is whole sub partition, need update partition table here */ if (pttype == TYPE_SUB_PT_WHOLE) { ptdev_read_table(ds->ptdev); } closedisk: close_disk(ds); end: return err; } /** * @brief flash one area using sparsing format * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_sparse_img(DL_STATE_T *ds, void *data, unsigned sz, uint8_t device_inst, uint64_t ptn, uint64_t size) { uint32_t i = 0; uint32_t fill_val = 0; uintptr_t data_end = (uintptr_t)data + sz; unsigned int chunk = 0; uint32_t *fill_buf = NULL; bool fill_buf_init = false; void *data_ptr_temp = NULL; void *data_back = data; uint32_t total_blocks = 0; uint64_t pos = 0; uint32_t blk_sz_actual = 0; uint64_t chunk_data_sz = 0; uint8_t *block_wrapper = NULL; uint64_t count_aligned = 0; chunk_header_t *chunk_header = NULL; sparse_header_t *sparse_header = NULL; uint64_t chunk_data_sz_remain = 0; block_wrapper = (uint8_t *)(dl_scratch_base); uint8_t j = device_inst; uint8_t md5_calc_bef[MD5_LEN] = {0}; uint8_t md5_calc_aft[MD5_LEN] = {0}; #if CONFIG_DLOADER_BLOCK_IO_MODE uint32_t sector_size = ds->erase_size; #endif DBG("Flashing sparse img\r\n"); if (!block_wrapper || ((addr_t)block_wrapper % ds->block_size) != 0 || (dl_scratch_sz % ds->block_size) != 0 || dl_scratch_sz == 0) { ERROR("aligned memory allocate fail!\r\n"); return ERR_SPARSE_IMAGE_MALLOC; } #if CONFIG_DLOADER_WITH_TRACE return ERR_SPARSE_IMAGE_DO_NOT_SUPPORT; #endif DBG("inst number=%d\r\n", j); DBG("size limit=%lld, ds->boot_offset(ptn)=%lld\r\n", size, ptn); total_blocks = 0; data = data_back; /* Read and skip over sparse image header */ sparse_header = (sparse_header_t *)data; if (!sparse_header->blk_sz || (sparse_header->blk_sz % 4)) { ERROR("block size error:%u\r\n", sparse_header->blk_sz); return ERR_INVALID_BLOCK_SIZE; } if (((uint64_t)sparse_header->total_blks * (uint64_t)sparse_header->blk_sz) > size) { ERROR("image too large :%llu\r\n", size); return ERR_IMAGE_TOO_LARGE; } data = (uint8_t *)data + sizeof(sparse_header_t); if (data_end < (uintptr_t)data) { ERROR("data end:%u header size:%zu\r\n", data_end, sizeof(sparse_header_t)); return ERR_SPARSE_IMAGE_BUFFERED; } if (sparse_header->file_hdr_sz != sizeof(sparse_header_t)) { ERROR("image header error!\r\n"); return ERR_SPARSE_IMAGE_HEADER; } DBG("=== Sparse Image Header ===\r\n"); DBG("magic: 0x%x\r\n", sparse_header->magic); DBG("major_version: 0x%x\r\n", sparse_header->major_version); DBG("minor_version: 0x%x\r\n", sparse_header->minor_version); DBG("file_hdr_sz: %d\r\n", sparse_header->file_hdr_sz); DBG("chunk_hdr_sz: %d\r\n", sparse_header->chunk_hdr_sz); DBG("blk_sz: %d\r\n", sparse_header->blk_sz); DBG("total_blks: %d\r\n", sparse_header->total_blks); DBG("total_chunks: %d\r\n", sparse_header->total_chunks); /* Start processing chunks */ for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) { /* Make sure the total image size does not exceed the partition size */ if (((uint64_t)total_blocks * (uint64_t)sparse_header->blk_sz) >= size) { ERROR("image too large:%llu!\r\n", size); return ERR_IMAGE_TOO_LARGE; } /* Read and skip over chunk header */ chunk_header = (chunk_header_t *)data; data = (uint8_t *)data + sizeof(chunk_header_t); if (data_end < (uintptr_t)data) { ERROR("data end:%u data:%p chunk header size:%zu\r\n", data_end, data, sizeof(chunk_header_t)); return ERR_IMAGE_TOO_LARGE; } DBG("=== Chunk Header ===\r\n"); DBG("chunk_type: 0x%x\r\n", chunk_header->chunk_type); DBG("chunk_data_sz/sparse_blk_sz: 0x%x\r\n", chunk_header->chunk_sz); DBG("total_size: 0x%x\r\n", chunk_header->total_sz); if (sparse_header->chunk_hdr_sz != sizeof(chunk_header_t)) { ERROR("chunk header error:%u!\r\n", sparse_header->chunk_hdr_sz); return ERR_SPARSE_IMAGE_CHUNK_HEADER; } chunk_data_sz = (uint64_t)sparse_header->blk_sz * chunk_header->chunk_sz; /* Make sure that the chunk size calculated from sparse image does not * exceed partition size */ if ((uint64_t)total_blocks * (uint64_t)sparse_header->blk_sz + chunk_data_sz > size) { ERROR("chunk data too large:%llu!\r\n", size); return ERR_SPARSE_IMAGE_CHUNK_TOO_LARGE; } switch (chunk_header->chunk_type) { case CHUNK_TYPE_RAW: if ((uint64_t)chunk_header->total_sz != ((uint64_t)sparse_header->chunk_hdr_sz + chunk_data_sz)) { ERROR("chunk size:%llu error!\r\n", chunk_data_sz); return ERR_SPARSE_IMAGE_CHUNK_NOT_MATCH; } if (data_end < (uintptr_t)data + chunk_data_sz) { ERROR("data end:%u data:%p chunk_data_sz:%llu\r\n", data_end, data, chunk_data_sz); return ERR_SPARSE_IMAGE_BUFFERED; } /* chunk_header->total_sz is uint32,So chunk_data_sz is now less than 2^32 otherwise it will return in the line above */ pos = ptn + ((uint64_t)total_blocks * sparse_header->blk_sz); count_aligned = 0; data_ptr_temp = data; chunk_data_sz_remain = chunk_data_sz; #if CONFIG_DLOADER_BLOCK_IO_MODE /* for ospi nor flash, erase the partition before writting */ if (pos == ptn && (ds->disk_type == NORFLASH)) { ASSERT(pos % sector_size == 0); if (dl_disk_erase(ds->disk_inst[j], pos, round_up((sparse_header->blk_sz * sparse_header->total_blks), sector_size))) { ERROR("disk_erase_group addr = 0x%llx error\r\n", pos); return ERR_PT_ERASE_FAIL; } } #endif while (chunk_data_sz_remain) { count_aligned = MIN(dl_scratch_sz, chunk_data_sz_remain); memset(block_wrapper, 0x0, dl_scratch_sz); memcpy(block_wrapper, data_ptr_temp, count_aligned); if (do_md5_rb_check) { md5((unsigned char *)(block_wrapper), count_aligned, (unsigned char *)md5_calc_bef); } if (dl_disk_write( ds->disk_inst[j], pos, block_wrapper, round_up(count_aligned, (uint64_t)ds->block_size))) { ERROR("flash storage error\r\n"); return ERR_PT_FLASH_FAIL; } if (do_md5_rb_check) { memset(block_wrapper, 0x0, dl_scratch_sz); if (dl_disk_read(ds->disk_inst[j], pos, block_wrapper, round_up(count_aligned, (uint64_t)ds->block_size))) { ERROR("flash storage error\r\n"); return ERR_PT_READ_FAIL; } md5((unsigned char *)(block_wrapper), count_aligned, (unsigned char *)md5_calc_aft); if (memcmp(md5_calc_aft, md5_calc_bef, MD5_LEN)) { ERROR("md5 check fail!\r\n"); dloader_hexdump(md5_calc_bef, MD5_LEN); dloader_hexdump(md5_calc_aft, MD5_LEN); return ERR_HASH_FAIL; } DBG("md5 check ok!\r\n"); } pos += count_aligned; data_ptr_temp = (uint8_t *)data_ptr_temp + count_aligned; chunk_data_sz_remain -= count_aligned; } if (total_blocks > (UINT_MAX - chunk_header->chunk_sz)) { ERROR("chunk size error:%u!\r\n", chunk_header->chunk_sz); return ERR_SPARSE_IMAGE_CHUNK_NOT_MATCH; } total_blocks += chunk_header->chunk_sz; data = (uint8_t *)data + (uint32_t)chunk_data_sz; break; case CHUNK_TYPE_FILL: if (chunk_header->total_sz != (sparse_header->chunk_hdr_sz + sizeof(uint32_t))) { ERROR("fill type error, size:%u!\r\n", chunk_header->total_sz); return ERR_SPARSE_IMAGE_CHUNK_NOT_MATCH; } blk_sz_actual = round_up(sparse_header->blk_sz, 32); /* Integer overflow detected */ if (blk_sz_actual < sparse_header->blk_sz) { ERROR("blk_sz_actual:%u error!\r\n", blk_sz_actual); return ERR_INVALID_BLOCK_SIZE; } fill_buf = (uint32_t *)(dl_scratch_base); if (!fill_buf) { ERROR(" allocat memory fail!\r\n"); return ERR_SPARSE_IMAGE_MALLOC; } if (data_end < (uintptr_t)data + sizeof(uint32_t)) { ERROR("data end:%u data:%p \r\n", data_end, data); return ERR_SPARSE_IMAGE_BUFFERED; } fill_val = *(uint32_t *)data; data = (char *)data + sizeof(uint32_t); chunk_data_sz_remain = chunk_header->chunk_sz * sparse_header->blk_sz; fill_buf_init = false; while (chunk_data_sz_remain) { if (dl_scratch_sz > chunk_data_sz_remain) { count_aligned = chunk_data_sz_remain; } else { count_aligned = dl_scratch_sz; } if (!fill_buf_init) { for (i = 0; !fill_buf_init && i < (count_aligned / sizeof(fill_val)); i++) { fill_buf[i] = fill_val; } fill_buf_init = true; } if (total_blocks > (UINT_MAX - chunk_header->chunk_sz)) { ERROR(" chunk size:%u error!\r\n", chunk_header->chunk_sz); return ERR_SPARSE_IMAGE_CHUNK_NOT_MATCH; } /* Make sure that the data written to partition does not exceed * partition size */ if ((uint64_t)total_blocks * (uint64_t)sparse_header->blk_sz + sparse_header->blk_sz > size) { ERROR(" size:%llu error!\r\n", size); return ERR_SPARSE_IMAGE_CHUNK_TOO_LARGE; } pos = ptn + ((uint64_t)total_blocks * sparse_header->blk_sz); #if CONFIG_DLOADER_BLOCK_IO_MODE /* for ospi nor flash, erase the partition before writting */ if (pos == ptn && (ds->disk_type == NORFLASH)) { ASSERT(pos % sector_size == 0); if (dl_disk_erase(ds->disk_inst[j], pos, round_up((sparse_header->blk_sz * sparse_header->total_blks), sector_size))) { ERROR("disk_erase_group addr = 0x%llx error\r\n", pos); return ERR_PT_ERASE_FAIL; } } #endif if (do_md5_rb_check) { md5((unsigned char *)(fill_buf), count_aligned, (unsigned char *)md5_calc_bef); } if (dl_disk_write( ds->disk_inst[j], pos, (uint8_t *)fill_buf, round_up(count_aligned, (uint64_t)ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } if (do_md5_rb_check) { memset(block_wrapper, 0x0, dl_scratch_sz); if (dl_disk_read(ds->disk_inst[j], pos, block_wrapper, round_up(count_aligned, (uint64_t)ds->block_size))) { ERROR("flash storage error\r\n"); return ERR_PT_READ_FAIL; } md5((unsigned char *)(fill_buf), count_aligned, (unsigned char *)md5_calc_aft); if (memcmp(md5_calc_aft, md5_calc_bef, MD5_LEN)) { ERROR("md5 check fail!\r\n"); dloader_hexdump(md5_calc_bef, MD5_LEN); dloader_hexdump(md5_calc_aft, MD5_LEN); return ERR_HASH_FAIL; } DBG("md5 check ok!\r\n"); } chunk_data_sz_remain -= count_aligned; total_blocks += count_aligned / sparse_header->blk_sz; } break; case CHUNK_TYPE_DONT_CARE: if (total_blocks > (UINT_MAX - chunk_header->chunk_sz)) { ERROR(" chunk size:%u error!\r\n", chunk_header->chunk_sz); return ERR_SPARSE_IMAGE_CHUNK_NOT_MATCH; } total_blocks += chunk_header->chunk_sz; break; case CHUNK_TYPE_CRC: if (chunk_header->total_sz != sparse_header->chunk_hdr_sz) { ERROR(" chunk total size:%u error!\r\n", chunk_header->total_sz); return ERR_SPARSE_IMAGE_CHUNK_NOT_MATCH; } if (total_blocks > (UINT_MAX - chunk_header->chunk_sz)) { ERROR(" chunk size:%u error!\r\n", chunk_header->chunk_sz); return ERR_SPARSE_IMAGE_CHUNK_NOT_MATCH; } total_blocks += chunk_header->chunk_sz; if ((uintptr_t)data > UINT_MAX - chunk_data_sz) { ERROR(" chunk data size:%llu error!\r\n", chunk_data_sz); return ERR_IMAGE_TOO_LARGE; } data = (uint8_t *)data + (uint32_t)chunk_data_sz; if (data_end < (uintptr_t)data) { ERROR("data end:%u data:%p \r\n", data_end, data); return ERR_SPARSE_IMAGE_BUFFERED; } break; default: ERROR("Unkown chunk type: %x\r\n", chunk_header->chunk_type); return ERR_SPARSE_IMAGE_CHUNK_UNKNOWN; } } DBG("Wrote %d blocks, expected to write %d blocks\r\n", total_blocks, sparse_header->total_blks); if (total_blocks != sparse_header->total_blks) { ERROR(" total block:%u error!\r\n", total_blocks); return ERR_PT_FLASH_FAIL; } return ERR_NONE; } /** * @brief read back data from disk and do md5 check * @param ds download disk states * @param data data buffer * @param sz size of data * @param read_addr read back address * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E md5_read_check(DL_STATE_T *ds, uint8_t inst, void *data, unsigned sz, uint64_t read_addr) { uint8_t md5_calc[MD5_LEN] = {0}; unsigned size = 0; struct MD5Context context; uint32_t i = 0; uint32_t cnt = 0; uint32_t left = 0; uint32_t length = 0; ASSERT(inst < EMMC_DEV_INST_MAX); size = sz; MD5Init(&context); cnt = size / dl_scratch_sz; left = size % dl_scratch_sz; for (i = 0; i <= cnt; i++) { if (i == cnt) { length = left; } else { length = dl_scratch_sz; } if (0 == length) break; memset((uint8_t *)(dl_scratch_base), 0x0, length); #if CONFIG_DLOADER_BLOCK_IO_MODE if (dl_disk_read(ds->disk_inst[inst], round_down(read_addr, ds->block_size), (uint8_t *)(dl_scratch_base), round_up(length, ds->block_size))) { ERROR("read back error!\r\n"); return ERR_HASH_FAIL; } #else if (dl_disk_read(ds->disk_inst[inst], read_addr, (uint8_t *)(dl_scratch_base), length)) { ERROR("read back error!\r\n"); return ERR_HASH_FAIL; } #endif MD5Update(&context, (uint8_t *)(dl_scratch_base), length); read_addr += length; } MD5Final(md5_calc, &context); #if CONFIG_DLOADER_WITH_TRACE /*when downloading with trace, we do not have md5 received from PC, so calc * here*/ if (data) md5(data, sz, md5_received); #endif if (memcmp(md5_received, md5_calc, MD5_LEN)) { ERROR("md5 check fail!\r\n"); dloader_hexdump(md5_received, MD5_LEN); dloader_hexdump(md5_calc, MD5_LEN); return ERR_HASH_FAIL; } dloader_hexdump(md5_calc, MD5_LEN); DBG("read back md5 check success!\r\n"); return ERR_NONE; } /** * @brief verify an addr directly * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E verify_addr_direct(DL_STATE_T *ds) { char *ptname = ds->ptname; uint64_t ptn = 0; uint64_t size = 0; char *addrsuffix; uint64_t verify_size = 0; DBG("verify an addr directly\r\n"); ptn = strtoull(ptname, &addrsuffix, 16); if ((ptn % ds->block_size) || (ptn > ds->capacity)) { ERROR("ptn alignment error = %llx\r\n", ptn); return ERR_PT_NOT_FOUND; } size = ds->capacity - ptn; verify_size = get_verify_size(ds->ptname); DBG("ptn:%lld size:%lld verify_size:%lld\r\n", ptn, size, verify_size); if ((verify_size > size) || (0 == verify_size)) { ERROR("verify_size error!\r\n"); return VERIFY_SIZE_ERROR; } return md5_read_check(ds, 0, NULL, verify_size, ptn); } /** * @brief flash an addr directly * @param ds download disk states * @param data data buffer * @param sz size of data * @param sparse_data use parse format or not * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_addr_direct(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { char *ptname = ds->ptname; uint64_t ptn = 0; uint64_t size = 0; char *addrsuffix; DBG("Flashing an addr directly\r\n"); ptn = strtoull(ptname, &addrsuffix, 16); if ((ptn % ds->block_size) || (ptn > ds->capacity)) { ERROR("ptn alignment error = %llx\r\n", ptn); return ERR_PT_NOT_FOUND; } #if CONFIG_DLOADER_BLOCK_IO_MODE if (ds->disk_type == NORFLASH) { if ((ptn % ds->erase_size)) { ERROR("ptn alignment to erase size error = %llx\r\n", ptn); return ERR_PT_NOT_FOUND; } } #endif size = ds->capacity - ptn; DBG("ptn:%lld size:%lld\r\n", ptn, size); if (sparse_data) { DBG("sparse donwload mode\r\n"); return flash_sparse_img(ds, data, sz, 0, ptn, size); } else { DBG("normal donwload mode\r\n"); if (round_up(sz, ds->erase_size) > size) { ERROR(" image too large:%llu!\r\n", round_up(sz, ds->erase_size)); return ERR_IMAGE_TOO_LARGE; } #if CONFIG_DLOADER_WITH_TRACE ptn += cmd_args->offset; DBG("ptn + cmd_args->offset is 0x%llx\r\n", ptn); if (cmd_args->total_sz > size) { ERROR("trace image total size too large:%llu!\r\n", cmd_args->total_sz); return ERR_IMAGE_TOO_LARGE; } if (ptn % ds->block_size) { ERROR("ptn alignment error in trace mode = %llx\r\n", ptn); return ERR_PT_BASE_ERROR; } #endif #if CONFIG_DLOADER_BLOCK_IO_MODE #if CONFIG_DLOADER_WITH_TRACE /* e3 do not have enough ram in block io mode, so make sure ptn is aligned to erase size and block size */ if ((ds->disk_type == NORFLASH) && (cmd_args->offset == 0)) { if (dl_disk_erase(ds->disk_inst[0], ptn, round_up(cmd_args->total_sz, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } } #else if (ds->disk_type == NORFLASH) { if (dl_disk_erase(ds->disk_inst[0], ptn, round_up(sz, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } } #endif #endif if (dl_disk_write(ds->disk_inst[0], ptn, (uint8_t *)data, round_up(sz, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } if (do_md5_rb_check) return md5_read_check(ds, 0, data, sz, ptn); return ERR_NONE; } } /** * @brief verify a normal partition * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E verify_normal_partition(DL_STATE_T *ds) { char *ptname = ds->ptname; char *sub_ptbname = ds->sub_ptbname; PARTITION_TYPE_E pttype = ds->partiton_type; struct partition_device *ptdev = ds->ptdev; char full_ptname[MAX_GPT_NAME_SIZE * 2 + 2] = {0}; uint64_t ptn = 0; uint64_t size = 0; uint64_t verify_size = 0; char *token1 = NULL; DBG("verify normal partition img\r\n"); verify_size = get_verify_size(ptname); if (strstr(ptname, ":")) { token1 = strchr((const char *)ptname, ':'); if (token1) { memset(token1, 0, strlen(token1)); } } else { ERROR("can't find verify_size in ptname %s!\r\n", ptname); return VERIFY_SIZE_ERROR; } if (pttype == TYPE_SUB_PT) { /* If the partition is sub partition, we should pass the full name */ snprintf(full_ptname, sizeof(full_ptname), "%s$%s", sub_ptbname, ptname); } else if (pttype == TYPE_SUB_PT_WHOLE) { snprintf(full_ptname, sizeof(full_ptname), "%s", sub_ptbname); } else { /* This is a primary partition */ snprintf(full_ptname, sizeof(full_ptname), "%s", ptname); } ptn = ptdev_get_offset(ptdev, full_ptname); if (ptn == 0 || ptn > ds->capacity) { ERROR("ptn size alignment error = %llx\r\n", ptn); return ERR_PT_NOT_FOUND; } size = ptdev_get_size(ptdev, full_ptname); if (size < ds->block_size || (size > (ds->capacity - ds->ptb_offset))) { ERROR("size error!\r\n"); return ERR_PT_NOT_FOUND; } if (ptn % ds->block_size || size % ds->block_size) { ERROR("ptn:%lld is not aligned to block size\r\n", ptn); return ERR_PT_OVERLAP; } if (ds->disk_type == NORFLASH) { if (ptn % ds->erase_size != 0 || size % ds->erase_size != 0) { ERROR("the size of the partition in nor flash is not aligned to " "erase group size\r\n"); return ERR_PT_OVERLAP; } } DBG("ptn:%lld size:%lld verify_size:%lld\r\n", ptn, size, verify_size); if ((verify_size > size) || (0 == verify_size)) { ERROR("verify_size error!\r\n"); return VERIFY_SIZE_ERROR; } return md5_read_check(ds, 0, NULL, verify_size, ptn); } /** * @brief flash a normal partition * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_normal_partition(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { char *ptname = ds->ptname; char *sub_ptbname = ds->sub_ptbname; PARTITION_TYPE_E pttype = ds->partiton_type; struct partition_device *ptdev = ds->ptdev; char full_ptname[MAX_GPT_NAME_SIZE * 2 + 2] = {0}; uint64_t ptn = 0; uint64_t size = 0; DBG("Flashing normal partition img\r\n"); if (pttype == TYPE_SUB_PT) { /* If the partition is sub partition, we should pass the full name */ snprintf(full_ptname, sizeof(full_ptname), "%s$%s", sub_ptbname, ptname); } else if (pttype == TYPE_SUB_PT_WHOLE) { snprintf(full_ptname, sizeof(full_ptname), "%s", sub_ptbname); } else { /* This is a primary partition */ snprintf(full_ptname, sizeof(full_ptname), "%s", ptname); } ptn = ptdev_get_offset(ptdev, full_ptname); if (ptn == 0 || ptn > ds->capacity) { ERROR("ptn size alignment error = %llx\r\n", ptn); return ERR_PT_NOT_FOUND; } size = ptdev_get_size(ptdev, full_ptname); if (size < ds->block_size || (size > (ds->capacity - ds->ptb_offset))) { ERROR("size error!\r\n"); return ERR_PT_NOT_FOUND; } if (ptn % ds->block_size || size % ds->block_size) { ERROR("ptn:%lld is not aligned to block size\r\n", ptn); return ERR_PT_OVERLAP; } if (ds->disk_type == NORFLASH) { if (ptn % ds->erase_size != 0 || size % ds->erase_size != 0) { ERROR("the size of the partition in nor flash is not aligned to " "erase group size\r\n"); return ERR_PT_OVERLAP; } } DBG("ptn:%lld size:%lld\r\n", ptn, size); if (sparse_data) { DBG("sparse donwload mode\r\n"); return flash_sparse_img(ds, data, sz, 0, ptn, size); } else { DBG("normal donwload mode\r\n"); if (round_up(sz, ds->erase_size) > size) { ERROR(" image too large:%llu!\r\n", round_up(sz, ds->block_size)); return ERR_IMAGE_TOO_LARGE; } #if CONFIG_DLOADER_WITH_TRACE ptn += cmd_args->offset; DBG("ptn + cmd_args->offset is 0x%llx\r\n", ptn); if (cmd_args->total_sz > size) { ERROR("trace image total size too large:%llu!\r\n", cmd_args->total_sz); return ERR_IMAGE_TOO_LARGE; } if ((ptn % ds->block_size)) { ERROR("ptn alignment error in trace mode = %llx\r\n", ptn); return ERR_PT_BASE_ERROR; } #endif #if CONFIG_DLOADER_BLOCK_IO_MODE #if CONFIG_DLOADER_WITH_TRACE /* e3 do not have enough ram in block io mode, so make sure ptn is aligned to erase size and block size */ if ((ds->disk_type == NORFLASH) && (cmd_args->offset == 0)) { if (dl_disk_erase(ds->disk_inst[0], ptn, round_up(cmd_args->total_sz, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } } #else if (ds->disk_type == NORFLASH) { if (dl_disk_erase(ds->disk_inst[0], ptn, round_up(sz, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } } #endif #endif if (dl_disk_write(ds->disk_inst[0], ptn, (uint8_t *)data, round_up(sz, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } if (do_md5_rb_check) return md5_read_check(ds, 0, data, sz, ptn); return ERR_NONE; } } /** * @brief verify emmc boot1 areas * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E verify_emmc_pack0(DL_STATE_T *ds) { uint8_t i = EMMC_BOOT1_DEV_INST; uint64_t ptn = 0; uint64_t size = 0; uint64_t verify_size = 0; size = EMMC_BOOT_PT_SIZE; ptn = 0; verify_size = get_verify_size(ds->ptname); DBG("verify emmc's boot0 package img, inst = %d\r\n", i); DBG("ptn:%lld size:%lld verify_size:%lld\r\n", ptn, size, verify_size); if ((verify_size > size) || (0 == verify_size)) { ERROR("verify_size error!\r\n"); return VERIFY_SIZE_ERROR; } return md5_read_check(ds, i, NULL, verify_size, ptn); } /** * @brief verify emmc boot2 areas * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E verify_emmc_pack1(DL_STATE_T *ds) { uint8_t i = EMMC_BOOT2_DEV_INST; uint64_t ptn = 0; uint64_t size = 0; uint64_t verify_size = 0; size = EMMC_BOOT_PT_SIZE; ptn = 0; verify_size = get_verify_size(ds->ptname); DBG("verify emmc's boot1 package img, inst = %d\r\n", i); DBG("ptn:%lld size:%lld verify_size:%lld\r\n", ptn, size, verify_size); if ((verify_size > size) || (0 == verify_size)) { ERROR("verify_size error!\r\n"); return VERIFY_SIZE_ERROR; } return md5_read_check(ds, i, NULL, verify_size, ptn); } /** * @brief verify emmc user areas with an offset 20k * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E verify_emmc_pack2(DL_STATE_T *ds) { uint8_t i = EMMC_USER_SPACE_DEV_INST; uint64_t ptn = 0; uint64_t size = 0; uint64_t verify_size = 0; DBG("verify emmc's boot2 package img, inst = %d\r\n", i); ptn = ptdev_get_offset(ds->ptdev, boot_package[BOOT2_NUM]); if (ptn == SD_SPL_OFFSET) { size = ptdev_get_size(ds->ptdev, boot_package[BOOT2_NUM]); } else { ptn = SD_SPL_OFFSET; size = ds->capacity - SD_SPL_OFFSET; } if (ptn == 0 || ptn > ds->capacity) { ERROR("ptn size alignment error = %llx\r\n", ptn); return ERR_PT_NOT_FOUND; } if (size < ds->block_size || (size > (ds->capacity - ds->ptb_offset))) { ERROR("size error!\r\n"); return ERR_PT_NOT_FOUND; } verify_size = get_verify_size(ds->ptname); DBG("ptn:%lld size:%lld verify_size:%lld\r\n", ptn, size, verify_size); if ((verify_size > size) || (0 == verify_size)) { ERROR("verify_size error!\r\n"); return VERIFY_SIZE_ERROR; } return md5_read_check(ds, i, NULL, verify_size, ptn); } /** * @brief flash emmc boot1 areas * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_emmc_pack0(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { uint8_t i = EMMC_BOOT1_DEV_INST; uint64_t ptn = 0; uint64_t size = 0; enum dl_err_code err = ERR_UNKNOWN; size = EMMC_BOOT_PT_SIZE; ptn = 0; DBG("Flashing emmc's boot0 package img, inst = %d\r\n", i); DBG("ptn:%lld size:%lld sz:%d\r\n", ptn, size, sz); if (sparse_data) { DBG("sparse donwload mode\r\n"); return flash_sparse_img(ds, data, sz, i, ptn, size); } else { DBG("normal donwload mode\r\n"); if (round_up(sz, ds->block_size) > size) { ERROR("boot image too large!\r\n"); return ERR_IMAGE_TOO_LARGE; } #if CONFIG_DLOADER_WITH_TRACE ptn += cmd_args->offset; DBG("ptn + cmd_args->offset is 0x%llx\r\n", ptn); if (cmd_args->total_sz > size) { ERROR("trace image total size too large:%llu!\r\n", cmd_args->total_sz); return ERR_IMAGE_TOO_LARGE; } if ((ptn % ds->block_size)) { ERROR("ptn alignment error in trace mode = %llx\r\n", ptn); return ERR_PT_BASE_ERROR; } #endif else if (dl_disk_write(ds->disk_inst[i], ptn, (uint8_t *)data, round_up(sz, ds->block_size))) { ERROR("write emmc error!\r\n"); return ERR_PT_FLASH_FAIL; } if (do_md5_rb_check) { err = md5_read_check(ds, i, data, sz, ptn); if (ERR_NONE != err) return err; } return ERR_NONE; } } /** * @brief flash emmc boot2 areas * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_emmc_pack1(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { uint8_t i = EMMC_BOOT2_DEV_INST; uint64_t ptn = 0; uint64_t size = 0; enum dl_err_code err = ERR_UNKNOWN; size = EMMC_BOOT_PT_SIZE; ptn = 0; DBG("Flashing emmc's boot1 package img, inst = %d\r\n", i); DBG("ptn:%lld size:%lld sz:%d\r\n", ptn, size, sz); if (sparse_data) { DBG("sparse donwload mode\r\n"); return flash_sparse_img(ds, data, sz, i, ptn, size); } else { DBG("normal donwload mode\r\n"); if (round_up(sz, ds->block_size) > size) { ERROR("boot image too large!\r\n"); return ERR_IMAGE_TOO_LARGE; } else if (dl_disk_write(ds->disk_inst[i], ptn, (uint8_t *)data, round_up(sz, ds->block_size))) { ERROR("write emmc error!\r\n"); return ERR_PT_FLASH_FAIL; } if (do_md5_rb_check) { err = md5_read_check(ds, i, data, sz, ptn); if (ERR_NONE != err) return err; } return ERR_NONE; } } /** * @brief flash emmc user areas with an offset 20k * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_emmc_pack2(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { uint8_t i = EMMC_USER_SPACE_DEV_INST; uint64_t ptn = 0; uint64_t size = 0; enum dl_err_code err = ERR_UNKNOWN; DBG("Flashing emmc's boot2 package img, inst = %d\r\n", i); ptn = ptdev_get_offset(ds->ptdev, boot_package[BOOT2_NUM]); if (ptn == SD_SPL_OFFSET) { size = ptdev_get_size(ds->ptdev, boot_package[BOOT2_NUM]); } else { ptn = SD_SPL_OFFSET; size = ds->capacity - SD_SPL_OFFSET; } if (ptn == 0 || ptn > ds->capacity) { ERROR("ptn size alignment error = %llx\r\n", ptn); return ERR_PT_NOT_FOUND; } if (size < ds->block_size || (size > (ds->capacity - ds->ptb_offset))) { ERROR("size error!\r\n"); return ERR_PT_NOT_FOUND; } DBG("ptn:%lld size:%lld sz:%d\r\n", ptn, size, sz); if (sparse_data) { DBG("sparse donwload mode\r\n"); return flash_sparse_img(ds, data, sz, i, ptn, size); } else { DBG("normal donwload mode\r\n"); if (round_up(sz, ds->block_size) > size) { ERROR("boot image too large!\r\n"); return ERR_IMAGE_TOO_LARGE; } else if (dl_disk_write(ds->disk_inst[i], ptn, (uint8_t *)data, round_up(sz, ds->block_size))) { ERROR("write emmc error!\r\n"); return ERR_PT_FLASH_FAIL; } if (do_md5_rb_check) { err = md5_read_check(ds, i, data, sz, ptn); if (ERR_NONE != err) return err; } return ERR_NONE; } } /** * @brief patch sfs information * @param ds download disk states * @param buffer data buffer * @param len size of data * @return true * @return false */ static void patch_sfs_crc(DL_STATE_T *ds, uint8_t *buffer, uint32_t len) { struct sfs sfs = {0}; uint32_t crc_val = 0; if (get_sfs_info(&sfs, buffer, len)) { ERROR("there is no available sfs in nor flash\r\n"); } crc_val = sfs_crc32(0, buffer, SFS_SIZE - 4); PUT_LONG(&buffer[SFS_CRC32_OFFSET], crc_val); } /** * @brief patch sfs information * @param ds download disk states * @param buffer data buffer * @param len size of data * @return true * @return false */ static bool patch_sfs(DL_STATE_T *ds, uint8_t *buffer, uint32_t len) { struct partition_device *ptdev = ds->ptdev; int i = 0; bool patched = false; struct sfs sfs = {0}; uint32_t crc_val = 0; uint64_t boot_1st_ptn = 0; uint64_t boot_2nd_ptn = 0; uint64_t boot_3rd_ptn = 0; uint32_t boot_1st_sfs_base = 0; uint32_t boot_2nd_sfs_base = 0; uint32_t boot_3rd_sfs_base = 0; const char *partition1[2] = {BOOT_PARTITION_NAME0, "boot"}; const char *partition2[2] = {BOOT_PARTITION_NAME1, "boot_back"}; const char *partition3[2] = {BOOT_PARTITION_NAME2, "boot_third"}; if (len < SFS_SIZE || !ptdev) { return false; } for (i = 0; i < 2; i++) { boot_1st_ptn = ptdev_get_offset(ptdev, partition1[i]); boot_2nd_ptn = ptdev_get_offset(ptdev, partition2[i]); boot_3rd_ptn = ptdev_get_offset(ptdev, partition3[i]); if ((!boot_1st_ptn || boot_1st_ptn >= UINT32_MAX) && (!boot_2nd_ptn || boot_2nd_ptn >= UINT32_MAX) && (!boot_3rd_ptn || boot_3rd_ptn >= UINT32_MAX)) { if (1 == i) { DBG("there is no boot partiton in nor flash's partition\r\n"); return false; } continue; } else break; } if (boot_1st_ptn >= UINT32_MAX) boot_1st_ptn = 0; if (boot_2nd_ptn >= UINT32_MAX) boot_2nd_ptn = 0; if (boot_3rd_ptn >= UINT32_MAX) boot_3rd_ptn = 0; DBG("boot_1st_ptn = %llx\r\n", boot_1st_ptn); DBG("boot_2nd_ptn = %llx\r\n", boot_2nd_ptn); DBG("boot_3rd_ptn = %llx\r\n", boot_3rd_ptn); if (get_sfs_info(&sfs, buffer, len)) { ERROR("there is no available sfs in nor flash\r\n"); return false; } boot_1st_sfs_base = GET_LWORD_FROM_BYTE(&buffer[SFS_NIA_OFFSET]); boot_2nd_sfs_base = GET_LWORD_FROM_BYTE(&buffer[SFS_BIA_OFFSET]); boot_3rd_sfs_base = GET_LWORD_FROM_BYTE(&buffer[SFS_TIA_OFFSET]); DBG("1st boot base sfs:0x%08x, partition:0x%08x\r\n", boot_1st_sfs_base, (uint32_t)boot_1st_ptn); DBG("2nd boot base sfs:0x%08x, partition:0x%08x\r\n", boot_2nd_sfs_base, (uint32_t)boot_2nd_ptn); DBG("3rd boot base sfs:0x%08x, partition:0x%08x\r\n", boot_3rd_sfs_base, (uint32_t)boot_3rd_ptn); if (boot_1st_sfs_base != (uint32_t)boot_1st_ptn) { PUT_LONG(&buffer[SFS_NIA_OFFSET], (uint32_t)boot_1st_ptn); DBG("sfs 1st image base update,original base:0x%08x -> base:0x%08x\r\n", boot_1st_sfs_base, (uint32_t)boot_1st_ptn); patched = true; } if (boot_2nd_sfs_base != (uint32_t)boot_2nd_ptn) { PUT_LONG(&buffer[SFS_BIA_OFFSET], (uint32_t)boot_2nd_ptn); DBG("sfs 2nd image base update, original base:0x%08x -> " "base:0x%08x\r\n", boot_2nd_sfs_base, (uint32_t)boot_2nd_ptn); patched = true; } if (boot_3rd_sfs_base != (uint32_t)boot_3rd_ptn) { PUT_LONG(&buffer[SFS_TIA_OFFSET], (uint32_t)boot_3rd_ptn); DBG("sfs 3rd try image base update, original base:0x%08x -> " "base:0x%08x\r\n", boot_3rd_sfs_base, (uint32_t)boot_3rd_ptn); patched = true; } if (patched) { crc_val = sfs_crc32(0, buffer, SFS_SIZE - 4); PUT_LONG(&buffer[SFS_CRC32_OFFSET], crc_val); } return patched; } /** * @brief flash Nor flash sfs areas * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_ospi_sfs_areas(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { uint64_t ptn = 0; const struct disk_norflash_info* flash_info; const struct spi_nor* flash; DBG("Flashing ospi's sfs img\r\n"); if (ds->partiton_type != TYPE_SAFETY_SFS_PT) { ERROR("pttype:%d error!\r\n", ds->partiton_type); return ERR_PT_NOT_FOUND; } if(is_msfs_data(data)) { flash_info = (struct disk_norflash_info*)(ds->disk_inst[0]->info->private_data); flash = (struct spi_nor*)(flash_info->config.norflash_dev); if(0 != get_matched_sfs(&(flash->info.flash_id[0]), (void **)&data, (uint32_t *)&sz)) { ERROR("get matched sfs from msfs error\r\n"); return ERR_GET_SFS_FORM_MSFS_FAIL; } } #if PTACH_SFS_CRC_WHEN_SFS_DOWNLOADING patch_sfs_crc(ds, data, sz); md5(data, sz, md5_received); #endif #if PTACH_SFS_WHEN_SFS_DOWNLOADING bool patched = false; patched = patch_sfs(ds, data, sz); md5(data, sz, md5_received); #endif DBG("ptn:%lld size:%d erase grp:%d\r\n", ptn, sz, ds->erase_size); if (SFS_RFD_RFU_SIZE > CONFIG_DL_SCRATCH_SIZE || (ptn + sz) > SFS_RFD_RFU_SIZE || ptn > SFS_RFD_RFU_SIZE || sz > SFS_RFD_RFU_SIZE) { ERROR("rfd size error\r\n"); return ERR_PT_FLASH_FAIL; } #if CONFIG_DLOADER_BLOCK_IO_MODE if (dl_disk_read(ds->disk_inst[0], 0, (uint8_t *)dl_scratch_base, round_up(SFS_RFD_RFU_SIZE, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } if (dl_disk_erase(ds->disk_inst[0], 0, round_up(SFS_RFD_RFU_SIZE, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } memcpy((uint8_t *)dl_scratch_base + ptn, data, sz); if (dl_disk_write(ds->disk_inst[0], 0, (uint8_t *)dl_scratch_base, round_up(SFS_RFD_RFU_SIZE, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } #else if (dl_disk_write(ds->disk_inst[0], ptn, data, sz)) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } #endif read_sfs(ds); if (do_md5_rb_check) return md5_read_check(ds, 0, data, sz, ptn); return ERR_NONE; } /** * @brief flash Nor flash rfd areas * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_ospi_rfd_areas(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { uint64_t ptn = RFD_OFFSET; DBG("Flashing ospi's rfd img\r\n"); if (ds->partiton_type != TYPE_SAFETY_RFD_PT) { ERROR("pttype:%d error!\r\n", ds->partiton_type); return ERR_PT_NOT_FOUND; } DBG("ptn:%lld size:%d erase grp:%d\r\n", ptn, sz, ds->erase_size); if (SFS_RFD_RFU_SIZE > CONFIG_DL_SCRATCH_SIZE || (ptn + sz) > SFS_RFD_RFU_SIZE || ptn > SFS_RFD_RFU_SIZE || sz > SFS_RFD_RFU_SIZE) { ERROR("rfd size error\r\n"); return ERR_PT_FLASH_FAIL; } #if CONFIG_DLOADER_BLOCK_IO_MODE if (dl_disk_read(ds->disk_inst[0], 0, (uint8_t *)dl_scratch_base, round_up(SFS_RFD_RFU_SIZE, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } if (dl_disk_erase(ds->disk_inst[0], 0, round_up(SFS_RFD_RFU_SIZE, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } memcpy((uint8_t *)dl_scratch_base + ptn, data, sz); if (dl_disk_write(ds->disk_inst[0], 0, (uint8_t *)dl_scratch_base, round_up(SFS_RFD_RFU_SIZE, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } md5((uint8_t *)dl_scratch_base, SFS_RFD_RFU_SIZE, md5_received); if (do_md5_rb_check) return md5_read_check(ds, 0, (uint8_t *)dl_scratch_base, SFS_RFD_RFU_SIZE, 0); #else if (dl_disk_write(ds->disk_inst[0], ptn, data, sz)) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } if (do_md5_rb_check) return md5_read_check(ds, 0, data, sz, ptn); #endif return ERR_NONE; } /** * @brief update sfs information according by parititon * @param ds download disk states * @return uint32_t 0 is success, 1 is fail */ static uint32_t update_sfs(DL_STATE_T *ds) { bool need_update = false; uint8_t *buffer = NULL; uint32_t ret = 0; uint32_t sfs_size = 0; if (ds->disk_type != NORFLASH) { return 1; } /* sfs must have an exclusive sector */ sfs_size = round_up(SFS_SIZE, ds->block_size); buffer = (uint8_t *)pvPortMallocAligned(sfs_size, ds->block_size); if (!buffer) { ERROR("allocate memory fail\r\n"); return 1; } memset(buffer, 0x0, sfs_size); if (dl_disk_read(ds->disk_inst[0], 0, buffer, sfs_size)) { ERROR("read nor flash fail\r\n"); ret = 1; goto end; } need_update = patch_sfs(ds, buffer, sfs_size); if (!need_update) goto end; if (dl_disk_erase(ds->disk_inst[0], 0, round_up(sfs_size, ds->erase_size))) { ERROR("erase sfs fail\r\n"); ret = 1; goto end; } if (dl_disk_write(ds->disk_inst[0], 0, buffer, sfs_size)) { ERROR("write sfs fail\r\n"); ret = 1; goto end; } ret = 0; end: if (buffer) vPortFree(buffer); return ret; } /** * @brief verify Nor flash Boot Package * @param ds download disk states * @param pack_num pack num * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E verify_ospi_pack(DL_STATE_T *ds, uint8_t pack_num) { uint64_t ptn = 0; uint64_t size = 0; uint64_t verify_size = get_verify_size(ds->ptname); DBG("verify norflash's boot%d package img, verfi_size = 0x%llx\r\n", pack_num, verify_size); ptn = ptdev_get_offset(ds->ptdev, boot_package[pack_num]); if (ptn != 0) { size = ptdev_get_size(ds->ptdev, boot_package[pack_num]); } else { ptn = sfs_get_image_base(ds, pack_num); if (ptn != 0) size = sfs_get_image_size_limit(ds, pack_num); else { ERROR("boot%d image base error, ptn = %lld!\r\n", pack_num, ptn); return ERR_PT_BASE_ERROR; } } if (ptn == 0 || ptn > ds->capacity) { ERROR("ptn size alignment error = %llx\r\n", ptn); return ERR_PT_NOT_FOUND; } if (size < ds->block_size || (size > ds->capacity)) { ERROR("size error!\r\n"); return ERR_PT_NOT_FOUND; } if (ptn % ds->block_size) { ERROR("ptn alignment error = %llx\r\n", ptn); return ERR_PT_BASE_ERROR; } if (size % ds->block_size) { ERROR("size alignment error = %llx\r\n", size); return ERR_PT_BASE_ERROR; } if ((verify_size > size) || (0 == verify_size)) { ERROR("verify_size error!\r\n"); return ERR_PT_NOT_FOUND; } DBG("ptn:%lld size:%lld\r\n", ptn, size); return md5_read_check(ds, 0, NULL, verify_size, ptn); } /** * @brief verify Nor flash Boot Package 0 * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E verify_ospi_pack0(DL_STATE_T *ds) { return verify_ospi_pack(ds, BOOT0_NUM); } /** * @brief verify Nor flash Boot Package 0 * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E verify_ospi_pack1(DL_STATE_T *ds) { return verify_ospi_pack(ds, BOOT1_NUM); } /** * @brief verify Nor flash Boot Package 0 * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E verify_ospi_pack2(DL_STATE_T *ds) { return verify_ospi_pack(ds, BOOT2_NUM); } /** * @brief flash Nor flash Boot Package * @param ds download disk states * @param data data buffer * @param sz size of data * @param sz pack num * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_ospi_pack(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data, uint8_t pack_num) { uint64_t ptn = 0; uint64_t size = 0; DBG("Flashing norflash's boot%d package img\r\n", pack_num); ptn = ptdev_get_offset(ds->ptdev, boot_package[pack_num]); if (ptn != 0) { size = ptdev_get_size(ds->ptdev, boot_package[pack_num]); } else { ptn = sfs_get_image_base(ds, pack_num); if (ptn != 0) size = sfs_get_image_size_limit(ds, pack_num); else { ERROR("boot%d image base error, ptn = %lld!\r\n", pack_num, ptn); return ERR_PT_BASE_ERROR; } } if (ptn == 0 || ptn > ds->capacity) { ERROR("ptn size alignment error = %llx\r\n", ptn); return ERR_PT_NOT_FOUND; } if (size < ds->block_size || (size > ds->capacity)) { ERROR("size error!\r\n"); return ERR_PT_NOT_FOUND; } if ((ptn % ds->block_size) || (ptn % ds->erase_size)) { ERROR("ptn alignment error = %llx\r\n", ptn); return ERR_PT_BASE_ERROR; } if ((size % ds->block_size) || (size % ds->erase_size)) { ERROR("size alignment error = %llx\r\n", size); return ERR_PT_BASE_ERROR; } DBG("ptn:%lld size:%lld erase grp:%d\r\n", ptn, size, ds->erase_size); if (sparse_data) { DBG("sparse donwload mode\r\n"); return flash_sparse_img(ds, data, sz, 0, ptn, size); } else { DBG("normal donwload mode\r\n"); if (round_up(sz, ds->erase_size) > size) { ERROR(" image too large:%llu!\r\n", round_up(sz, ds->block_size)); return ERR_IMAGE_TOO_LARGE; } #if CONFIG_DLOADER_WITH_TRACE ptn += cmd_args->offset; DBG("ptn + cmd_args->offset is 0x%llx\r\n", ptn); if (cmd_args->total_sz > size) { ERROR("trace image total size too large:%llu!\r\n", cmd_args->total_sz); return ERR_IMAGE_TOO_LARGE; } if (ptn % ds->block_size) { ERROR("ptn alignment error in trace mode = %llx\r\n", ptn); return ERR_PT_BASE_ERROR; } #endif #if CONFIG_DLOADER_BLOCK_IO_MODE #if CONFIG_DLOADER_WITH_TRACE if ((ds->disk_type == NORFLASH) && (cmd_args->offset == 0)) { if (dl_disk_erase(ds->disk_inst[0], ptn, round_up(cmd_args->total_sz, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } } #else if (ds->disk_type == NORFLASH) { if (dl_disk_erase(ds->disk_inst[0], ptn, round_up(sz, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } } #endif #endif if (dl_disk_write(ds->disk_inst[0], ptn, data, round_up(sz, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } if (do_md5_rb_check) return md5_read_check(ds, 0, data, sz, ptn); return ERR_NONE; } } /** * @brief flash Nor flash Boot Package Normal * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_ospi_pack0(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { return flash_ospi_pack(ds, data, sz, sparse_data, BOOT0_NUM); } /** * @brief flash Nor flash Boot Package Backup * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_ospi_pack1(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { return flash_ospi_pack(ds, data, sz, sparse_data, BOOT1_NUM); } /** * @brief flash Nor flash Boot Package Third * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_ospi_pack2(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { return flash_ospi_pack(ds, data, sz, sparse_data, BOOT2_NUM); } /** * @brief flash the GUID parition table for disk * @param ds download disk states * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E flash_gpt_table(DL_STATE_T *ds, void *data, unsigned sz, uint8_t sparse_data) { uint32_t ret = PTB_CHECK_ERROR; char *ptname = ds->ptname; char *sub_ptbname = ds->sub_ptbname; PARTITION_TYPE_E pttype = ds->partiton_type; struct partition_device *ptdev = ds->ptdev; bool last_part_extend = true; uint64_t gpt_sz = 0; DBG("Flashing gpt table img\r\n"); if (ds->disk_type == NORFLASH) { last_part_extend = false; } ret = check_partition_table(ds, data, sz); DBG("check_partition_table ret:%d\r\n", ret); if (ret == PTB_CHECK_ERROR) { ERROR("check error ret:%u !\r\n", ret); if (strlen(sub_ptbname)) return ERR_SUB_PTB_NOT_MATCH; else return ERR_PRI_PTB_NOT_MATCH; } else if (ret == PTB_NO_NEED_FLASH) { DBG("partition table doesn't need to flash\r\n"); goto end; } /* This is primary GPT table. To write primary ptb, set sub_ptbname = NULL */ if (pttype == TYPE_PRI_PTB) sub_ptbname = NULL; DBG("Attempt to write primary/sub partition:%s \r\n", ptname); DBG("ptb_offset:0x%llx\r\n", ds->ptb_offset); /* If flashing ospi nor flash, it may re-calculate ptb offset and reset * ptdev, so get ptdev pointer here */ ptdev = ds->ptdev; if (ptdev && ds->disk_type == NORFLASH) { gpt_sz = GET_PRI_GPT_SIZE(ds->block_size); /* * For nor flash, it needs to erase storage before writting data to it. * To reduce time consumption, it only erases primary gpt header and * backup gpt header. Note, the addr and the length are must be aligned * to erase group size. * */ dl_disk_erase(ds->disk_inst[0], ds->ptb_offset, round_up(gpt_sz, ds->erase_size)); dl_disk_erase( ds->disk_inst[0], round_down(ds->capacity - gpt_sz + ds->block_size, ds->erase_size), round_up(gpt_sz - ds->block_size, ds->erase_size)); DBG("ptb_offset:0x%0llx gpt_sz:%llu block size:%d capacity:0x%0llx " "\r\n", ds->ptb_offset, gpt_sz, ds->block_size, ds->capacity); } if (!ptdev || ptdev_write_table(ptdev, sub_ptbname, sz, (unsigned char *)data, last_part_extend)) { ERROR("write gpt table error pt:%s ptb_offset:0x%llx\r\n", ptname, ds->ptb_offset); return ERR_PT_FLASH_FAIL; } end: if (ds->disk_type == NORFLASH) { DBG("update sfs\r\n"); ret = update_sfs(ds); if (ret) { ERROR("updata sfs fail\r\n"); return ERR_PT_FLASH_FAIL; } } return ERR_NONE; } static bool parse_direct_add_name(const char *sub_ptbname, const char *ptname) { if ((strlen(ptname) <= strlen(ADD_DIR_FUZZY_NAME)) || (strlen(sub_ptbname))) return 0; return !strncmp(ptname, ADD_DIR_FUZZY_NAME, strlen(ADD_DIR_FUZZY_NAME)); } /** * @brief get the partition type * @param sub_ptbname * @param ptname * @return PARTITION_TYPE_E partition type */ static PARTITION_TYPE_E parse_pt_type(const char *sub_ptbname, const char *ptname) { uint8_t i = 0; bool parse_sub_pt = strlen(sub_ptbname) ? 1 : 0; bool parse_pt = strlen(ptname) ? 1 : 0; char ptname_temp[MAX_GPT_NAME_SIZE + 1] = {0}; char *token1 = NULL; /* sub partition table name and ptname can not be null simultaneously */ if (!parse_sub_pt && !parse_pt) { ERROR("partition is TYPE_PT_UNKNOWN\r\n"); return TYPE_PT_UNKNOWN; } strncpy((char *)ptname_temp, ptname, strlen(ptname)); if (strstr(ptname_temp, ":")) { token1 = strchr((const char *)ptname_temp, ':'); if (token1) memset(token1, 0, strlen(token1)); } /* get the partition type */ for (i = 0; i < ARRAY_SIZE(dl_type_table); i++) { if (parse_sub_pt ^ dl_type_table[i].is_sub_pt) continue; if (!strcmp(ptname_temp, dl_type_table[i].ptname)) { DBG("get partiton type = %s\r\n", dl_type_table[i].type_name); return dl_type_table[i].partiton_type; } } if (parse_direct_add_name(sub_ptbname, ptname_temp)) { DBG("get partiton type = adrress direct\r\n"); return TYPE_ADD_DIRECT; } else if (!parse_sub_pt) { DBG("get partiton type = primary parition\r\n"); return TYPE_PRI_PT; } else if (!parse_pt) { DBG("get partiton type = sub parition all\r\n"); return TYPE_SUB_PT_WHOLE; } else { DBG("get partiton type = sub parition\r\n"); return TYPE_SUB_PT; } } /** * @brief close disk * @param download disk states * @return uint32_t 0 is success, 1 is failed */ static bool close_disk(DL_STATE_T *ds) { if (!ds) { ERROR("ds is null\r\n"); return 1; } if (disk_close(ds->disk_inst[0])) { return 1; } if (ds->disk_inst[1] && disk_close(ds->disk_inst[1])) { return 1; } if (ds->disk_inst[2] && disk_close(ds->disk_inst[2])) { return 1; } return 0; } /** * @brief open disk * @param download disk states * @return bool 0 is success, 1 is failed */ static bool open_disk(DL_STATE_T *ds) { if (!ds) { ERROR("ds is null\r\n"); return 1; } close_disk(ds); if (disk_open(ds->disk_name, ds->disk_inst[0])) { ERROR("open %s for ds->disk_inst[0] failed \r\n", ds->disk_name); return 1; } if (ds->disk_inst[1] && disk_open(ds->disk_boot1_name, ds->disk_inst[1])) { ERROR("open %s for ds->disk_inst[1] failed \r\n", ds->disk_boot1_name); return 1; } if (ds->disk_inst[2] && disk_open(ds->disk_boot2_name, ds->disk_inst[2])) { ERROR("open %s for ds->disk_inst[2] failed \r\n", ds->disk_boot2_name); return 1; } return 0; } /** * @brief parse the target information that is from fastboot command * @param full_ptname target information * @return DL_STATE_T* download disk states */ DL_STATE_T *parse_partition_name(const char *full_ptname, DL_ERR_CODE_E *err) { char *token1 = NULL; char *token2 = NULL; uint32_t token_num = 0; uint32_t full_len = 0; uint32_t sub_ptb_len = 0; uint32_t pt_len = 0; char disk_name[MAX_GPT_NAME_SIZE + 1] = {0}; char sub_ptbname[MAX_GPT_NAME_SIZE + 1] = {0}; char short_ptname[MAX_GPT_NAME_SIZE + 1] = {0}; *err = ERR_NONE; DL_STATE_T *ds = NULL; DBG("-------------------Parsing name------------------------\r\n"); if (!full_ptname || (full_len = strlen(full_ptname)) == 0) { ERROR("can not get the partition name for download\r\n"); *err = ERR_PTNAME_NOT_EXIST; goto fail; } for (uint32_t i = 0; i < full_len; i++) { if (full_ptname[i] == '$') { token_num++; } } /* full name:xxx$yyy$zzz */ if (token_num != 2) { ERROR("partition name format error, should be xxx$$zzz\r\n"); *err = ERR_PT_FULL_NAME_FORMAT; goto fail; } token1 = strchr(full_ptname, '$'); token2 = strrchr(full_ptname, '$'); strncpy(disk_name, full_ptname, token1 - full_ptname); strncpy(sub_ptbname, token1 + 1, token2 - token1 - 1); strncpy(short_ptname, token2 + 1, full_len - (token2 + 1 - full_ptname)); DBG("disk_name:%s sub_ptb:%s sub_pt:%s\r\n", disk_name, sub_ptbname, short_ptname); #if CONFIG_DLOADER_FLASH if (!strcmp(disk_name, OSPI1_STORAGE_NAME) && flash1_init_result == STORAGE_INIT_RESULT_FAIL) { ERROR("%s init fail, can not download\r\n", disk_name); *err = ERR_FLASH1_INIT_FAIL; goto fail; } #endif #if CONFIG_DLOADER_EMMC if (!strcmp(disk_name, EMMC1_STORAGE_NAME) && emmc1_init_result == STORAGE_INIT_RESULT_FAIL) { ERROR("%s init fail, can not download\r\n", disk_name); *err = ERR_EMMC1_INIT_FAIL; goto fail; } #endif #if CONFIG_DLOADER_SD if (!strcmp(disk_name, SD1_STORAGE_NAME) && sd1_init_result == STORAGE_INIT_RESULT_FAIL) { ERROR("%s init fail, can not download\r\n", disk_name); *err = ERR_SD1_INIT_FAIL; goto fail; } #endif for (uint32_t i = 0; i < sizeof(dl_state_table) / sizeof(dl_state_table[0]); i++) { if (!strcmp(dl_state_table[i].download_name, disk_name)) { DBG("ds = dl_state_table[%d]\r\n", i); ds = &dl_state_table[i]; break; } } if (ds == NULL) { ERROR("can't find a matched disk for download\r\n"); *err = ERR_DISK_NOT_EXIST; goto fail; } if (open_disk(ds)) { ERROR("open the download disk failed\r\n"); *err = ERR_DISK_OPEN_ERROR; goto fail; } disk_set_block_size(ds->disk_inst[0], CONFIG_DLOADER_DISK_BLOCK_SIZE); ds->block_size = disk_get_block_size(ds->disk_inst[0]); ds->erase_size = disk_erase_size(ds->disk_inst[0]); ds->capacity = disk_size(ds->disk_inst[0]); #if CONFIG_DLODER_USE_RAM_AS_DISK if (!strcmp(ds->disk_name, DISK_RAM_NAME(0))) { ds->erase_size = 4096; } if (!strcmp(ds->disk_name, DISK_RAM_NAME(1))) { ds->erase_size = 524288; } #endif ASSERT(ds->block_size != 0); ASSERT(ds->erase_size != 0); ASSERT(ds->block_size == CONFIG_DLOADER_DISK_BLOCK_SIZE); ASSERT(ds->erase_size % ds->block_size == 0); ASSERT(ds->capacity % ds->block_size == 0); ASSERT(ds->capacity > ds->block_size); ASSERT(ds->capacity > ds->erase_size); ASSERT(ds->capacity > ds->ptb_offset); ASSERT(ds->capacity > (GET_PRI_GPT_SIZE(ds->block_size))); if (ds->disk_type == NORFLASH) { ds->ptb_offset = ds->erase_size * NOR_FLASH_PTB_SECTOR_INDEX; } else { ds->ptb_offset = 0; } if (!(ds->ptdev) && ds->ptb_offset != INVALID_PTB_OFFSET) { ds->ptdev = ptdev_setup(ds->disk_inst[0], ds->ptb_offset); if (ds->ptdev) { DBG("read partition table\r\n"); ptdev_read_table(ds->ptdev); } else { ERROR("get ptdev:%s error\r\n", disk_name); *err = ERR_PARTITION_READ_ERROR; goto closedisk; } } ds->partiton_type = parse_pt_type(sub_ptbname, short_ptname); if (ds->partiton_type == TYPE_PT_UNKNOWN) { goto closedisk; } pt_len = strlen(short_ptname); sub_ptb_len = strlen(sub_ptbname); memset(ds->sub_ptbname, 0x0, sizeof(ds->sub_ptbname)); memset(ds->ptname, 0x0, sizeof(ds->ptname)); strncpy(ds->sub_ptbname, sub_ptbname, sub_ptb_len); strncpy(ds->ptname, short_ptname, pt_len); DBG("download_name = %s\r\n", ds->download_name); DBG("disk_name = %s\r\n", ds->disk_name); DBG("disk_boot1_name = %s\r\n", ds->disk_boot1_name); DBG("disk_boot2_name = %s\r\n", ds->disk_boot2_name); DBG("disk_type = %d\r\n", ds->disk_type); DBG("block_size = %d\r\n", ds->block_size); DBG("erase_size = %d\r\n", ds->erase_size); DBG("capacity = %lld\r\n", ds->capacity); DBG("ptb_offset = %lld\r\n", ds->ptb_offset); DBG("partiton_type = %d\r\n", ds->partiton_type); DBG("ptname = %s\r\n", ds->ptname); DBG("sub_ptbname = %s\r\n", ds->sub_ptbname); DBG("boot_offset = %lld\r\n", ds->boot_offset); return ds; closedisk: close_disk(ds); fail: return NULL; } /** * @brief dloader flash comman prosess * @param fb fastboot hanlder * @param arg input argument * @param data data buffer * @param sz size of data * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E dl_cmd_flash(const char *arg, void *data, unsigned sz) { DL_ERR_CODE_E err = ERR_NONE; static uint32_t flash_cnt = 0; uint8_t md5_calc[MD5_LEN] = {0}; uint8_t i = 0; PARTITION_TYPE_E pttype = TYPE_PT_UNKNOWN; struct command_table *downloader = NULL; sparse_header_t *sparse_header = NULL; sparse_header = (sparse_header_t *)data; DL_ERR_CODE_E(*flash_cmd) (DL_STATE_T * ds, void *data, unsigned sz, uint8_t sparse_data) = NULL; DL_STATE_T *ds = NULL; #if (CONFIG_HYPERBUS_MODE == 1) && (CONFIG_DLOADER_PROG_HYPER_FLASH_FUSE == 1) static uint32_t fuse_once = 0; uint32_t val = 0; #endif /* get flash name */ ds = parse_partition_name(arg, &err); if (!ds || err != ERR_NONE) { goto end; } ASSERT(ds); DBG("------------------flash_cnt = %d-------------------\r\n", flash_cnt++); /* check recived data's md5 */ if (do_md5_rcv_check) { md5(data, sz, md5_calc); if (memcmp(md5_received, md5_calc, MD5_LEN)) { ERROR("md5 check fail!\r\n"); dloader_hexdump(md5_received, MD5_LEN); dloader_hexdump(md5_calc, MD5_LEN); err = ERR_HASH_FAIL_FROM_PC; goto closedisk; } DBG("calc md5 check ok!\r\n"); } /* do flashing work */ for (i = 0; i < ARRAY_SIZE(dl_cmd_table); i++) { if (ds->partiton_type == dl_cmd_table[i].partiton_type) { if (ds->disk_type == dl_cmd_table[i].disk_type || dl_cmd_table[i].disk_type == ALLDISK) { downloader = &dl_cmd_table[i]; DBG("find download cmd, i = %d\r\n", i); break; } } } if (!downloader) { ERROR("can not find command in cmd_table\r\n"); err = ERR_CMD_ERROR; goto closedisk; } flash_cmd = downloader->flash_callback; if (flash_cmd) { DBG("flash_cmd function = %s\r\n", downloader->download_cmd_name); if (sparse_header->magic != SPARSE_HEADER_MAGIC) err = flash_cmd(ds, data, sz, 0); else err = flash_cmd(ds, data, sz, 1); if (err) { ERROR("flash error\r\n"); goto closedisk; } } else { ERROR("flash_cmd is NULL\r\n"); err = CAN_NOT_FIND_A_DOWNLOAD_FUNCTION; goto closedisk; } /* If the image is whole sub partition, need update partition table here */ if (pttype == TYPE_SUB_PT_WHOLE) { ptdev_read_table(ds->ptdev); } #if (CONFIG_HYPERBUS_MODE == 1) && (CONFIG_DLOADER_PROG_HYPER_FLASH_FUSE == 1) /* If the disk is hyper flash, program the hyper flash fuse bit */ if (ds->disk_type == NORFLASH && fuse_once == 0 && err == ERR_NONE) { fuse_once = 1; if (0 != sdrv_fuse_sense(HYPER_FLASH_MODE_FUSE_INDEX, &val)) { ERROR("Failed to read hyper flash fuse %d\r\n", HYPER_FLASH_MODE_FUSE_INDEX); err = HYPER_FLASH_FUSE_READ_ERROR; goto closedisk; } DBG("read hyper flash fuse %d val 0x%08x\r\n", HYPER_FLASH_MODE_FUSE_INDEX, val); if ((val & HYPER_FLASH_MODE_FUSE_VAL) == 0) { val |= HYPER_FLASH_MODE_FUSE_VAL; DBG("write hyper flash fuse %d val 0x%08x\r\n", HYPER_FLASH_MODE_FUSE_INDEX, val); if (0 != sdrv_fuse_program(HYPER_FLASH_MODE_FUSE_INDEX, val)) { ERROR("Failed to write hyper flash fuse %d val 0x%08x\r\n", HYPER_FLASH_MODE_FUSE_INDEX, val); err = HYPER_FLASH_FUSE_WRITE_ERROR; goto closedisk; } if (0 != sdrv_fuse_sense(HYPER_FLASH_MODE_FUSE_INDEX, &val)) { ERROR("Failed to read hyper flash fuse %d\r\n", HYPER_FLASH_MODE_FUSE_INDEX); err = HYPER_FLASH_FUSE_READ_ERROR; goto closedisk; } DBG("read back hyper flash fuse %d val 0x%08x\r\n", HYPER_FLASH_MODE_FUSE_INDEX, val); if ((val & HYPER_FLASH_MODE_FUSE_VAL) != HYPER_FLASH_MODE_FUSE_VAL) { ERROR("read back error, hyper flash fuse %d val 0x%08x\r\n", HYPER_FLASH_MODE_FUSE_INDEX, val); err = HYPER_FLASH_FUSE_VAL_ERROR; goto closedisk; } } else { DBG("no need to fuse\r\n"); } } #endif closedisk: close_disk(ds); end: return err; } /** * @brief erase emmc's ont boot area by the part number * @param ds download disk states * @param inst_num emmc dev number * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_emmc_one_boot_area(DL_STATE_T *ds, uint8_t inst_num) { uint64_t ptn = 0; uint64_t size = 0; struct disk_dev *dev = NULL; ASSERT(inst_num < EMMC_DEV_INST_MAX); ASSERT(ds->disk_inst[inst_num]); dev = ds->disk_inst[inst_num]; size = EMMC_BOOT_PT_SIZE; ptn = ds->boot_offset; /* for sdcard and emmc'user areas, the boot offset is 20k */ if (inst_num == EMMC_USER_SPACE_DEV_INST) { ptn = ptdev_get_offset(ds->ptdev, boot_package[BOOT2_NUM]); if (ptn == SD_SPL_OFFSET) { size = ptdev_get_size(ds->ptdev, boot_package[BOOT2_NUM]); } else { ptn = SD_SPL_OFFSET; size = BPT_SIZE; } if (size < ds->block_size) { ERROR("image size error, size = %lld!\r\n", size); return ERR_PT_SIZE_ERROR; } } DBG("erase emmc inst_num = %d\r\n", inst_num); DBG("erase ptn:%lld size:%lld!\r\n", ptn, size); if (dl_disk_erase(dev, ptn, round_up(size, ds->block_size))) { ERROR("erase data error!\r\n"); return ERR_PT_ERASE_FAIL; } return ERR_NONE; } /** * @brief erase emmc's boot1 space * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_emmc_pack0(DL_STATE_T *ds) { enum dl_err_code err = ERR_UNKNOWN; DBG("erase_emmc_pack0\r\n"); /* The current storage is emmc card */ if (!ds->boot_offset) { err = erase_emmc_one_boot_area(ds, EMMC_BOOT1_DEV_INST); if (err) { ERROR("erase_emmc_pack0 error!\r\n"); return err; } } /* The current storage is sd card */ else { DBG("sd card do not have pack0\r\n"); } return ERR_NONE; } /** * @brief erase emmc's boot2 space * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_emmc_pack1(DL_STATE_T *ds) { enum dl_err_code err = ERR_UNKNOWN; DBG("erase_emmc_pack1\r\n"); /* The current storage is emmc card */ if (!ds->boot_offset) { err = erase_emmc_one_boot_area(ds, EMMC_BOOT2_DEV_INST); if (err) { ERROR("erase_emmc_pack1 error!\r\n"); return err; } } /* The current storage is sd card */ else { DBG("sd card do not have pack1\r\n"); } return ERR_NONE; } /** * @brief erase the 20k byte offset of emmc's user space * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_emmc_pack2(DL_STATE_T *ds) { enum dl_err_code err = ERR_UNKNOWN; DBG("erase_emmc_pack2\r\n"); /* The current storage is emmc card */ err = erase_emmc_one_boot_area(ds, EMMC_USER_SPACE_DEV_INST); if (err) { ERROR("erase_emmc_pack2 error!\r\n"); return err; } return ERR_NONE; } /** * @brief erase the Nor flash sfs areas * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_ospi_sfs_areas(DL_STATE_T *ds) { uint64_t ptn = 0; uint64_t size = 0; PARTITION_TYPE_E pttype = ds->partiton_type; DBG("erase sfs\r\n"); if (pttype != TYPE_SAFETY_SFS_PT) { ERROR("pttype:%d error!\r\n", pttype); return ERR_PT_NOT_FOUND; } ptn = 0; size = SFS_SIZE; DBG("erase ptn:%lld size:%lld!\r\n", ptn, size); #if CONFIG_DLOADER_BLOCK_IO_MODE if (dl_disk_read(ds->disk_inst[0], 0, (uint8_t *)dl_scratch_base, round_up(SFS_RFD_RFU_SIZE, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } if (dl_disk_erase(ds->disk_inst[0], 0, round_up(SFS_RFD_RFU_SIZE, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } memset((uint8_t *)dl_scratch_base + ptn, 0xFF, SFS_SIZE); if (dl_disk_write(ds->disk_inst[0], 0, (uint8_t *)dl_scratch_base, round_up(SFS_RFD_RFU_SIZE, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } #else if (dl_disk_erase(ds->disk_inst[0], ptn, size)) { ERROR(" erase data error!\r\n"); return ERR_PT_ERASE_FAIL; } #endif return ERR_NONE; } /** * @brief erase the Nor flash rfd areas * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_ospi_rfd_areas(DL_STATE_T *ds) { uint64_t ptn = RFD_OFFSET; uint64_t size = RFD_SIZE; PARTITION_TYPE_E pttype = ds->partiton_type; DBG("erase rdf\r\n"); if (pttype != TYPE_SAFETY_RFD_PT) { ERROR("pttype:%d error!\r\n", pttype); return ERR_PT_NOT_FOUND; } DBG("erase ptn:%lld size:%lld!\r\n", ptn, size); #if CONFIG_DLOADER_BLOCK_IO_MODE if (dl_disk_read(ds->disk_inst[0], 0, (uint8_t *)dl_scratch_base, round_up(SFS_RFD_RFU_SIZE, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } if (dl_disk_erase(ds->disk_inst[0], 0, round_up(SFS_RFD_RFU_SIZE, ds->erase_size))) { ERROR("erase partition fail\r\n"); return ERR_PT_ERASE_FAIL; } memset((uint8_t *)dl_scratch_base + ptn, 0xFF, RFD_SIZE); if (dl_disk_write(ds->disk_inst[0], 0, (uint8_t *)dl_scratch_base, round_up(SFS_RFD_RFU_SIZE, ds->block_size))) { ERROR(" write data error!\r\n"); return ERR_PT_FLASH_FAIL; } #else if (dl_disk_erase(ds->disk_inst[0], ptn, size)) { ERROR(" erase data error!\r\n"); return ERR_PT_ERASE_FAIL; } #endif return ERR_NONE; } /** * @brief erase the Nor flash Boot package areas * @param ds download disk states * @param pack_num package number * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_ospi_pack(DL_STATE_T *ds, uint8_t pack_num) { uint64_t ptn = 0; uint64_t size = 0; DBG("erase ospi_pack %d\r\n", pack_num); ptn = ptdev_get_offset(ds->ptdev, boot_package[pack_num]); if (ptn != 0) { size = ptdev_get_size(ds->ptdev, boot_package[pack_num]); } else { ptn = sfs_get_image_base(ds, pack_num); if (ptn != 0) size = BPT_SIZE; else { ERROR("image base error, ptn = %lld!\r\n", ptn); return ERR_PT_BASE_ERROR; } } if (!size) { ERROR("image size error, size = %lld!\r\n", size); return ERR_PT_SIZE_ERROR; } DBG("erase ptn:%lld size:%lld!\r\n", ptn, size); if (ptn % ds->erase_size || ((size % ds->erase_size) && (size != BPT_SIZE))) { ERROR("the size of the partition in nor flash is not aligned to erase " "group size\r\n"); return ERR_PT_OVERLAP; } if (dl_disk_erase(ds->disk_inst[0], ptn, round_up(size, ds->erase_size))) { ERROR(" erase data error!\r\n"); return ERR_PT_ERASE_FAIL; } return ERR_NONE; } /** * @brief erase the Nor flash Firtst Boot package areas * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_ospi_pack0(DL_STATE_T *ds) { return erase_ospi_pack(ds, BOOT0_NUM); } /** * @brief erase the Nor flash Second Boot package areas * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_ospi_pack1(DL_STATE_T *ds) { return erase_ospi_pack(ds, BOOT1_NUM); } /** * @brief erase the Nor flash Third Boot package areas * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_ospi_pack2(DL_STATE_T *ds) { return erase_ospi_pack(ds, BOOT2_NUM); } /** * @brief erase the GUID parition table * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_gpt_table(DL_STATE_T *ds) { uint64_t gpt_sz = 0; uint64_t capacity = 0; const char *ptname = NULL; PARTITION_TYPE_E pttype = TYPE_PT_UNKNOWN; uint64_t ptn_pri = 0; uint64_t ptn_bak = 0; const char *sub_ptbname = NULL; uint64_t size_pri = 0; uint64_t size_bak = 0; struct partition_device *ptdev = NULL; char full_ptname[MAX_GPT_NAME_SIZE * 2 + 2] = {0}; pttype = ds->partiton_type; ptname = ds->ptname; sub_ptbname = ds->sub_ptbname; ptdev = ds->ptdev; capacity = ds->capacity; DBG("ptb_offset:0x%llx\r\n", ds->ptb_offset); /* MBR is in the first LBA. GPT header is in the second LBA. GPT entries are * in the following blocks */ gpt_sz = GET_PRI_GPT_SIZE(ds->block_size); size_pri = gpt_sz; if (pttype == TYPE_SUB_PTB) { snprintf(full_ptname, sizeof(full_ptname), "%s", sub_ptbname); ptn_pri = ptdev_get_offset(ptdev, full_ptname); size_bak = ptdev_get_size(ptdev, full_ptname); ptn_bak = ptn_pri + size_bak - gpt_sz + ds->block_size; } else { snprintf(full_ptname, sizeof(full_ptname), "%s", ptname); ptn_pri = ds->ptb_offset; ptn_bak = ds->capacity - gpt_sz + ds->block_size; } /* back up gpt header no mbr block */ size_bak = gpt_sz - ds->block_size; DBG("ptn_pri:0x%llx size_pri:0x%llx ptn_bak:0x%llx size_bak:0x%llx " "cap:0x%llx!\r\n", ptn_pri, size_pri, ptn_bak, size_bak, capacity); if (ds->disk_type == NORFLASH) { size_pri = round_up(size_pri, ds->erase_size); size_bak = round_up(size_bak, ds->erase_size); ptn_bak = round_down(ptn_bak, ds->erase_size); if (ptn_pri % ds->erase_size != 0) { ERROR("nor flash ptn must be aligned to erase group size!\r\n"); return ERR_PT_ERASE_FAIL; } } /* erase primary gpt header */ if (dl_disk_erase(ds->disk_inst[0], ptn_pri, size_pri)) { ERROR("erase primary gpt header error!\r\n"); return ERR_PT_ERASE_FAIL; } /* erase back up gpt header */ if (dl_disk_erase(ds->disk_inst[0], ptn_bak, size_bak)) { ERROR(" erase backup gpt header error!\r\n"); return ERR_PT_ERASE_FAIL; } ptdev_read_table(ds->ptdev); return ERR_NONE; } /** * @brief erase a normal partition in PTB * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_addr_direct(DL_STATE_T *ds) { return ERR_NONE; } /** * @brief erase a normal partition in PTB * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_normal_partition(DL_STATE_T *ds) { const char *ptname = NULL; uint32_t block_size = 0; PARTITION_TYPE_E pttype = ds->partiton_type; uint64_t ptn = 0; uint64_t size = 0; const char *sub_ptbname = NULL; struct partition_device *ptdev = NULL; char full_ptname[MAX_GPT_NAME_SIZE * 2 + 2] = {0}; ptdev = ds->ptdev; block_size = ds->block_size; DBG("ptb_offset:0x%llx\r\n", ds->ptb_offset); /* For ospi nor flash, if ptdev is null, * it indicates there is no gpt in nor flash. * */ if (!ptdev) { ERROR("ospi flash has no gpt\r\n"); return ERR_NONE; } ptname = ds->ptname; sub_ptbname = ds->sub_ptbname; if (pttype == TYPE_SUB_PT_WHOLE) { snprintf(full_ptname, sizeof(full_ptname), "%s", sub_ptbname); } else if (pttype == TYPE_SUB_PT) { snprintf(full_ptname, sizeof(full_ptname), "%s$%s", sub_ptbname, ptname); } else { snprintf(full_ptname, sizeof(full_ptname), "%s", ptname); } ptn = ptdev_get_offset(ptdev, full_ptname); size = ptdev_get_size(ptdev, full_ptname); if (size < block_size || ptn == 0 || ptn > ds->capacity || (size > (ds->capacity - ds->ptb_offset))) { ERROR("partition not found ptn:%lld size:%lld! name:%s\r\n", ptn, size, full_ptname); return ERR_PT_NOT_FOUND; } if (ptn % block_size || size % block_size) { ERROR("ptn:%lld is not aligned to block size\r\n", ptn); return ERR_PT_OVERLAP; } if (ds->disk_type == NORFLASH) { if (ptn % ds->erase_size != 0 || size % ds->erase_size != 0) { ERROR("the size of the partition in nor flash is not aligned to " "erase group size\r\n"); return ERR_PT_OVERLAP; } } DBG("erase ptn:%lld size:%lld!\r\n", ptn, size); if (dl_disk_erase(ds->disk_inst[0], ptn, size)) { ERROR(" erase data error!\r\n"); return ERR_PT_ERASE_FAIL; } return ERR_NONE; } /** * @brief erass all the disk * @param ds download disk states * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E erase_all(DL_STATE_T *ds) { enum dl_err_code err = ERR_UNKNOWN; DBG("all erase\r\n"); if (!ds->boot_offset && ds->disk_type == MMC) { /* The current storage is emmc */ err = erase_emmc_one_boot_area(ds, EMMC_BOOT1_DEV_INST); if (err) { ERROR("erase boot1 error!\r\n"); return err; } err = erase_emmc_one_boot_area(ds, EMMC_BOOT2_DEV_INST); if (err) { ERROR("erase boot2 error!\r\n"); return err; } } if (dl_disk_erase(ds->disk_inst[0], 0, ds->capacity)) { ERROR("erase all data error!\r\n"); return ERR_PT_ERASE_FAIL; } return ERR_NONE; } /** * @brief dloader erase process action * @param arg input fastboot argument * @param data data buffer * @param sz data buffer size * @return DL_ERR_CODE_E error code */ DL_ERR_CODE_E dl_cmd_erase(const char *arg, void *data, unsigned sz) { static uint32_t erase_cnt = 0; uint8_t i = 0; DL_ERR_CODE_E err = ERR_NONE; struct command_table *downloader = NULL; DL_ERR_CODE_E (*erase_cmd)(DL_STATE_T *ds) = NULL; DL_STATE_T *ds = NULL; ds = parse_partition_name(arg, &err); if (!ds || err != ERR_NONE) { goto end; } ASSERT(ds); DBG("------------------erase_cnt = %d-------------------\r\n", erase_cnt++); /* do flashing work */ for (i = 0; i < ARRAY_SIZE(dl_cmd_table); i++) { if (ds->partiton_type == dl_cmd_table[i].partiton_type) { if (ds->disk_type == dl_cmd_table[i].disk_type || dl_cmd_table[i].disk_type == ALLDISK) { downloader = &dl_cmd_table[i]; DBG("find erase cmd, i = %d\r\n", i); break; } } } if (!downloader) { ERROR("can not find command in cmd_table\r\n"); err = ERR_CMD_ERROR; goto closedisk; } erase_cmd = downloader->erase_cmd_callback; if (erase_cmd) { DBG("erase_cmd function = %s\r\n", downloader->erase_cmd_name); err = erase_cmd(ds); if (err) { ERROR("flash error\r\n"); } } else { ERROR("erase_cmd is NULL\r\n"); err = CAN_NOT_FIND_A_ERASE_FUNCTION; } closedisk: close_disk(ds); end: return err; } #ifdef CONFIG_DLOADER_WITH_USB /** * @brief fastboot flash command * @param fb fastboot hanlder * @param arg input argument * @param data data buffer * @param sz size of data */ static void fastboot_cmd_flash(fastboot_t *fb, const char *arg, void *data, unsigned sz) { DL_ERR_CODE_E err = ERR_UNKNOWN; err = dl_cmd_flash(arg, data, sz); if (err) { ERROR("flash error\r\n"); fastboot_common_fail(fb, response_error(err, arg)); } fastboot_common_okay(fb, ""); } /** * @brief fastboot erase command * @param fb fastboot hanlder * @param arg input argument * @param data data buffer * @param sz size of data */ static void fastboot_cmd_erase(fastboot_t *fb, const char *arg, void *data, unsigned sz) { DL_ERR_CODE_E err = ERR_UNKNOWN; err = dl_cmd_erase(arg, data, sz); if (err) { ERROR("erase error\r\n"); fastboot_common_fail(fb, response_error(err, arg)); } fastboot_common_okay(fb, ""); } static void mark_boot_pin_flag(uint8_t boot_pin) { uint32_t val = 0; val = sdrv_rstgen_read_general(&reset_general_reg_rom_ctrl); val |= ((boot_pin << ROM_CTRL_BOOT_PIN_OVERRIDE_BIT_OFFSET) & 0x1E); val |= (0x1 << ROM_CTRL_BOOT_PIN_OVERRIDE_ENABLE_BIT_OFFSET); sdrv_rstgen_write_general(&reset_general_reg_rom_ctrl, val); } static void clear_boot_pin_flag() { uint32_t val = 0; val = sdrv_rstgen_read_general(&reset_general_reg_rom_ctrl); val &= ~0x1E; val &= ~(0x1 << ROM_CTRL_BOOT_PIN_OVERRIDE_ENABLE_BIT_OFFSET); sdrv_rstgen_write_general(&reset_general_reg_rom_ctrl, val); } /** * @brief fastboot reboot command * @param fb fastboot hanlder * @param arg input argument * @param data data buffer * @param sz size of data */ static void fastboot_cmd_reboot(fastboot_t *fb, const char *arg, void *data, unsigned sz) { DBG("fastboot reboot device arg = %s\r\n", arg); if (strstr(arg, "bootloader")) { mark_boot_pin_flag(8); } else { clear_boot_pin_flag(); } sdrv_rstgen_global_reset(&rstctl_glb); fastboot_common_okay(fb, ""); } /** * @brief fastboot recv md5 command * @param fb fastboot hanlder * @param arg input argument * @param data data buffer * @param sz size of data */ static void fastboot_cmd_md5(fastboot_t *fb, const char *arg, void *data, unsigned sz) { char md5_received_str[MD5_LEN * 2 + 1] = {0}; memset(md5_received, 0x0, MD5_LEN); memcpy(md5_received_str, arg, MD5_LEN * 2); str2hex(md5_received_str, MD5_LEN * 2, md5_received, MD5_LEN); do_md5_rcv_check = true; DBG("receive md5 from pc\r\n"); dloader_hexdump(md5_received, MD5_LEN); fastboot_common_okay(fb, ""); } /** * @brief fastboot read fuse command * @param fb fastboot hanlder * @param arg input argument * @param data data buffer * @param sz size of data */ static void fastboot_cmd_read_fuse(fastboot_t *fb, const char *arg, void *data, unsigned sz) { uint32_t fuse_val = 0; uint32_t fuse_index = 0; char fuse_response[MAX_RSP_SIZE] = {0}; fuse_index = strtoul(arg, NULL, 16); DBG("arg:%s fuse_index = 0x%x!\r\n", arg, fuse_index); if (errno != 0 || fuse_index > FUSE_INDEX_MAX) { ERROR("index error:%d!\r\n", errno); fastboot_common_fail(fb, response_error(ERR_EFUSE_INDEX, "")); return; } if (0 != sdrv_fuse_sense(fuse_index, &fuse_val)) { ERROR("failed to sense fuse 0x%x\r\n", fuse_index); fastboot_common_fail(fb, response_error(ERR_EFUSE_READ, "")); return; } DBG("fuse_0x%x = 0x%08x\r\n", fuse_index, fuse_val); snprintf(fuse_response, sizeof(fuse_response), "eFuseRead:%08x:%08x", fuse_index, fuse_val); fastboot_common_okay(fb, fuse_response); return; } /** * @brief Get the efuse args object * @param arg input arguement * @param index fuse index * @param val fuseval * @return 0 is success */ static uint32_t get_efuse_args(const char *arg, uint32_t *index, uint32_t *val) { uint32_t arg_len = 0; const char token = ':'; uint32_t val_str_len = 0; const char *val_pos = NULL; const char *md5_pos = NULL; uint32_t md5_str_len = 0; uint8_t md5_r[MD5_LEN] = {0}; uint32_t index_str_len = 0; uint8_t md5_calc[MD5_LEN] = {0}; char val_str[UINT32_HEX_STR_LEN + 1] = {0}; char index_str[UINT32_HEX_STR_LEN + 1] = {0}; val_pos = strchr(arg, token); md5_pos = strrchr(arg, token); if (!val_pos || val_pos == md5_pos) { ERROR("arg error:%s!\r\n", arg); return 1; } arg_len = strlen(arg); index_str_len = val_pos - arg; val_str_len = md5_pos - val_pos - 1; md5_str_len = arg_len - index_str_len - val_str_len - 2; if (index_str_len > UINT32_HEX_STR_LEN || val_str_len > UINT32_HEX_STR_LEN || md5_str_len != MD5_LEN * 2) { ERROR("arg len error:%s!\r\n", arg); return 2; } str2hex(md5_pos + 1, md5_str_len, md5_r, MD5_LEN); md5((const unsigned char *)arg, val_str_len + index_str_len + 1, md5_calc); if (memcmp(md5_calc, md5_r, MD5_LEN)) { ERROR("md5 check fail!\r\n"); dloader_hexdump(md5_r, MD5_LEN); dloader_hexdump(md5_calc, MD5_LEN); return 3; } strncpy(index_str, arg, index_str_len); strncpy(val_str, val_pos + 1, val_str_len); *index = strtoul(index_str, NULL, 16); if (errno || *index > FUSE_INDEX_MAX) { ERROR("strtoul error:%d!\r\n", errno); return 4; } *val = strtoul(val_str, NULL, 16); if (errno) { ERROR("strtoul error:%d!\r\n", errno); return 5; } return 0; } /** * @brief fastboot flash fuse command * @param fb fastboot hanlder * @param arg input argument * @param data data buffer * @param sz size of data */ static void fastboot_cmd_flash_fuse(fastboot_t *fb, const char *arg, void *data, unsigned sz) { uint32_t ret = 0; uint32_t fuse_val = 0; uint32_t fuse_index = 0; ret = get_efuse_args(arg, &fuse_index, &fuse_val); if (ret) { ERROR("index ret:%d!\r\n", ret); fastboot_common_fail(fb, response_error(ERR_EFUSE_INDEX, "")); return; } DBG("fuse index:0x%08x val:0x%08x\r\n", fuse_index, fuse_val); ret = sdrv_fuse_program(fuse_index, fuse_val); if (ret) { ERROR("burn efuse fail:%d!\r\n", ret); fastboot_common_fail(fb, response_error(ERR_EFUSE_BURN, "")); return; } fastboot_common_okay(fb, ""); return; } /** * @brief Get the serialno number * @return const char* */ static const char *get_serialno(void) { return FB_USBD_DevCfg.SerialNbrStrPtr; } /** * @brief Get the product name * @return const char* */ static const char *get_product(void) { return FB_USBD_DevCfg.ProductStrPtr; } /** * @brief fastboot verify command * @param fb fastboot hanlder * @param arg input argument * @param data data buffer * @param sz size of data */ static void fastboot_cmd_verify(fastboot_t *fb, const char *arg, void *data, unsigned sz) { DL_ERR_CODE_E err = ERR_UNKNOWN; err = dl_cmd_verify(arg, data, sz); if (err) { ERROR("flash verify\r\n"); fastboot_common_fail(fb, response_error(err, arg)); } fastboot_common_okay(fb, ""); } /** * @brief register fastboot commands * @param dl_buf_sz data buffer size */ static void register_commands(uint32_t dl_buf_sz) { static char max_download_size[MAX_RSP_SIZE] = {0}; /* Publish fastboot cmd */ fastboot_register_cmd("flash:", fastboot_cmd_flash); fastboot_register_cmd("erase:", fastboot_cmd_erase); fastboot_register_cmd("reboot", fastboot_cmd_reboot); fastboot_register_cmd("md5:", fastboot_cmd_md5); fastboot_register_cmd("eFuseRead:", fastboot_cmd_read_fuse); fastboot_register_cmd("eFuseProgram:", fastboot_cmd_flash_fuse); fastboot_register_cmd("verify:", fastboot_cmd_verify); fastboot_register_cmd("fuse:", fastboot_cmd_fuse_bin); /* publish variables and their values */ fastboot_register_var("product", get_product()); fastboot_register_var("serialno", get_serialno()); fastboot_register_var("dloader-version", DLOADER_VERSION); fastboot_register_var("dev-stage", "dloader"); /* Max download size supported */ snprintf(max_download_size, MAX_RSP_SIZE, "\t0x%x", dl_buf_sz); fastboot_register_var("max-download-size", (const char *)max_download_size); } #endif // DLOADER_WITH_USB #if CONFIG_DLOADER_WITH_TRACE /** * @brief dump trace command */ static void dump_cmd(void) { DBG("cmd:%p \r\n", cmd_args); DBG("storage :%10s\r\n", cmd_args->storage_name); DBG("partition :%10s\r\n", cmd_args->name); DBG("total size :%10llu\r\n", cmd_args->total_sz); DBG("offset :%10llu\r\n", cmd_args->offset); DBG("current size :%10llu\r\n", cmd_args->cur_sz); DBG("download flag:%10llu\r\n\r\n", cmd_args->dl_status); } /** * @brief Trace process thread * @param arg not used */ static void trace_process(void *arg) { DL_ERR_CODE_E err = ERR_UNKNOWN; char pt_name[256] = {0}; while (true) { /* wait recv event happen */ while (!(cmd_args->dl_status)) { // DBG("trace process waiting\r\n"); osDelay(osKernelGetTickFreq() / 10); } cmd_args->dl_status = 0; prog_status->ret_code = 1; prog_status->flag = 0; dump_cmd(); memset(pt_name, 0, 256); sprintf(pt_name, "%s$$%s", cmd_args->storage_name, cmd_args->name); err = dl_cmd_flash(pt_name, dl_data_base, cmd_args->cur_sz); if (!err) { prog_status->ret_code = 0; DBG("trace flash success %p\r\n", &prog_status->ret_code); } else { ERROR("trace flash error, %s", err_info[err]); } prog_status->flag = 1; } } #endif static void print_dloader_info(void) { static uint8_t print_info_flag = 0; if (print_info_flag == 0) { DBG("dloader version %s\r\n", DLOADER_VERSION); uint32_t support_flash_num = 0; #if (CONFIG_HYPERBUS_MODE == 1) const struct flash_info *support_flash_info = sdrv_spi_nor_get_flash_table(1, &support_flash_num); #else const struct flash_info *support_flash_info = sdrv_spi_nor_get_flash_table(0, &support_flash_num); #endif if (support_flash_info != NULL) { DBG("support flash table:\r\n"); for (uint32_t i = 0; i < support_flash_num; i++) { ssdk_printf(SSDK_CRIT, "%s", support_flash_info[i].name); ssdk_printf(SSDK_CRIT, " "); } ssdk_printf(SSDK_CRIT, "\r\n"); } else { ERROR("get support flash table fail\r\n"); } print_info_flag = 1; } } /** * @brief dloader init * @return 0 is success , other is fail */ int dloader_init(void) { print_dloader_info(); #if CONFIG_DLOADER_BLOCK_IO_MODE DBG("Block Io Mode\r\n"); #else DBG("Cached Io Mode\r\n"); #endif dl_scratch_sz = CONFIG_DL_SCRATCH_SIZE; #if CONFIG_E3104 dl_scratch_base = (void *)0x4E0000; #else dl_scratch_base = (void *)pvPortMallocAligned(dl_scratch_sz, DL_ALIGN_SIZE); #endif if (!dl_scratch_base) { ERROR("dl_scratch_base align failed\r\n"); return -1; } dl_data_sz = CONFIG_DL_DATA_BUF_SIZE; #if CONFIG_E3104 dl_data_base = (void *)0x4F0000; #else dl_data_base = (void *)pvPortMallocAligned(dl_data_sz, DL_ALIGN_SIZE); #endif if (!dl_data_base) { ERROR("dl_data_base align failed\r\n"); return -1; } DBG("dl_scratch_base:0x%p dl_scratch_sz: 0x%08x\r\n", dl_scratch_base, dl_scratch_sz); DBG("dl_data_base: 0x%p dl_data_sz: 0x%08x\r\n", dl_data_base, dl_data_sz); do_md5_rb_check = true; do_md5_rcv_check = false; #ifdef CONFIG_DLOADER_WITH_USB DBG("USB init\r\n"); register_commands(dl_data_sz); fb_data = fastboot_common_init((void *)dl_data_base, dl_data_sz); if (!fb_data) { ERROR("fastboot_common_init failed\r\n"); return -1; } #endif #if CONFIG_DLOADER_WITH_TRACE prog_status = (PROG_STATUS_T *)CONFIG_TRACE_PROG_STATUS_BASE; if (!prog_status) { ERROR("prog_status osAllocAlign failed\r\n"); return -1; } cmd_args = (PROG_CMD_ARGS_T *)CONFIG_TRACE_CMD_BASE; if (!cmd_args) { ERROR("cmd_args osAllocAlign failed\r\n"); return -1; } DBG("Trace Program Status Memory Base = 0x%x\r\n", CONFIG_TRACE_PROG_STATUS_BASE); DBG("Trace Command Memory Base = 0x%x\r\n", CONFIG_TRACE_CMD_BASE); arch_disable_cache(UCACHE); memset(prog_status, 0x0, sizeof(PROG_STATUS_T)); memset(cmd_args, 0x0, sizeof(PROG_CMD_ARGS_T)); cmd_args->elf_status = ELF_STATUS_MAGIC; cmd_args->data_base = (uint32_t)dl_data_base; cmd_args->data_size = dl_data_sz; ASSERT(dl_data_sz >= DLOADER_STANDARD_SIZE); if (!trace_process_thread) { osThreadAttr_t attr = {0}; attr.name = (const char *)"trace_process"; attr.stack_size = TRACE_PROCESS_STACK_SIZE; attr.priority = osPriorityNormal; trace_process_thread = osThreadNew(trace_process, NULL, &attr); if (!trace_process_thread) { ERROR("failed to dloader_test process thread\r\n"); return -1; } } DBG("Start dloader for trace!\r\n"); return 0; #endif return 0; }