4591 lines
130 KiB
C
4591 lines
130 KiB
C
/**
|
|
* @file dloader.c
|
|
*
|
|
* Copyright (c) 2021 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*
|
|
* Description: device downloader program
|
|
*
|
|
* Revision History:
|
|
* -----------------
|
|
*/
|
|
#include <FreeRTOS.h>
|
|
#include <armv7-r/cache.h>
|
|
#include <board.h>
|
|
#include <ctype.h>
|
|
#include <debug.h>
|
|
#include <disk.h>
|
|
#include <disk_norflash.h>
|
|
#include <dloader_fuse_bin.h>
|
|
#include <errno.h>
|
|
#include <list.h>
|
|
#include <md5.h>
|
|
#include <param.h>
|
|
#include <part.h>
|
|
#include <partition_parser.h>
|
|
#include <sd_boot_img.h>
|
|
#include <sdrv_fuse.h>
|
|
#include <sdrv_spi_nor.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <types.h>
|
|
|
|
#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;
|
|
}
|