// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2019 * Broadcom Corp */ #include #include #include #include #include #include #include #include #include "bca_common.h" #include "bcm_bootstate.h" #include "spl_env.h" #include "bca_sdk.h" #include "bcm_secure.h" #include "httpd/bcmbca_net.h" #include #include #if defined(CONFIG_WDT) #include #endif #if defined(CONFIG_BCMBCA_PMC) #include "pmc_drv.h" #endif #define SIZE_OVERHEAD 0x100000UL //1M #define BOOTFS_SIZE_BYTES 0x00A00000UL //10M #define ROOTFS_SIZE_BYTES 0x02800000UL //40M #define SQUASHFS_MAGIC 0x73717368 #define UBIFS_MAGIC 0x06101831 #define MIN_IMG_INDEX 1 #define MAX_IMG_INDEX 2 #define atoi(s) simple_strtol(s, NULL, 0) DECLARE_GLOBAL_DATA_PTR; /* Flag indicating whether we want to force an update and disregard any compatibility checks */ static int forced_updates = 0; static char forced_image_media[256] = {0}; static char forced_boot_media[256] = {0}; /* Flag to prevent synching of runtime env to loader bin when flashing a new loader */ static int disable_runtime_env_sync = 0; typedef int (*flash_fn)( ulong addr, ulong size , int img_index); typedef struct { char name[20]; /* Name of upgrade bundle component */ flash_fn func; /* Function to flash this upgrade bundle component */ } flashfn_table_entry; /* Enable CLI commands based on included CMD modules */ #if defined(CONFIG_CMD_GPT) && defined(CONFIG_CMD_PART) && defined(CONFIG_CMD_MMC) && defined(CONFIG_MMC) #define BCA_SDK_EMMC_CMD 1 #endif #if defined(CONFIG_CMD_UBI) && defined(CONFIG_CMD_MTD) && defined(CONFIG_NAND) #define BCA_SDK_NAND_CMD 1 #endif #if defined(CONFIG_CMD_SF) && defined(CONFIG_SPI_FLASH) && defined(CONFIG_DM_SPI_FLASH) && defined(CONFIG_CMD_MTD) #define BCA_SDK_SPINOR_CMD 1 #endif #if defined(BCA_SDK_SPINOR_CMD) static int spinor_load_bootfs(uint32_t bootfs_load_addr ); static int spinor_restoredefault(void); static int write_spinor_partition( char *const partitionname, ulong addr, ulong size ); static int do_flash_spinor_binary(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int do_flash_spinor_bootfs_rootfs(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int flash_loader_spinor( ulong addr, ulong size); #endif #ifdef BCA_SDK_EMMC_CMD /* EMMC specific routines */ static int flash_rootfs_emmc( ulong addr, ulong size, int img_index ); static int flash_bootfs_emmc( ulong addr, ulong size, int img_index ); static int flash_loader_emmc( ulong addr, ulong size, int img_index ); static int set_emmc_metadata( char* metadata, int size ); static int get_emmc_metadata( char* metadata, int size ); static int emmc_load_bootfs( int img_index, uint32_t bootfs_load_addr ); static int emmc_rdwr_userdata_part( char * part_name, ulong addr, ulong size, int write ); static int emmc_rdwr_boot_part( ulong addr, ulong size, int img_index, int write ); static int emmc_restoredefault(void); static int emmc_do_gpt_fixup(void); #endif /* BCA_SDK_EMMC_CMD */ #ifdef BCA_SDK_NAND_CMD /* NAND Specific routines */ static int flash_rootfs_nand( ulong addr, ulong size, int img_index ); static int flash_bootfs_nand( ulong addr, ulong size, int img_index ); static int flash_loader_nand( ulong addr, ulong size, int img_index ); static int set_nand_metadata( char* metadata, int size ); static int get_nand_metadata( char* metadata, int size ); static int nand_load_fit( int img_index, uint32_t fit_load_addr ); static int nand_load_bootfs( int img_index, uint32_t bootfs_load_addr ); static int nand_restoredefault(void); #endif /* BCA_SDK_NAND_CMD */ /* Generic routines */ static unsigned int bcm_handle_mapper(void* fit, char *flash_device, char *flash_opts); static int update_flash_parts_from_loader_bin( ulong loader_addr, ulong loader_size ); static int get_binary_from_bundle( ulong bundle_addr, char * conf_name, char * name, char ** bin_name, ulong * addr, ulong * size ); static int sync_update_loader_bin_env(ulong loader_addr); static int verify_compat_string( const char * compat_str ); static int do_flash_bins(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int do_boot(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int do_load(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int load_linux_img(int flag, int argc, char *const argv[]); static int do_restoredefault(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int do_metadata(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int do_force(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int flash_rootfs( ulong addr, ulong size , int img_index); static int flash_bootfs( ulong addr, ulong size , int img_index); static int flash_loader( ulong addr, ulong size , int img_index); static int do_flash_loader(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int do_flash_bootfs_rootfs( char * bootfs_filename, char * rootfs_filename, int image_index ); static int do_flash_upgrade_img (cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); static int set_metadata_val( int * committed, int * valid, int * seq ); static int get_metadata_val( int * committed, int * valid, int * seq ); static int do_activate(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); char * get_loader_media(void); static char * get_image_media(void); static int get_active_img_idx(void); static int set_active_img_idx( int img_idx); #ifdef CONFIG_BCMBCA_HTTPD static int do_httpd_start(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); #endif #ifdef CONFIG_BCMBCA_XRDP_ETH static int do_eth_active_port(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]); #endif /* Local Variables */ static flashfn_table_entry fn_table[] = { { "bootfs", flash_bootfs }, { "rootfs", flash_rootfs }, { "loader", flash_loader } }; #ifdef CONFIG_BCMBCA_BOARD_SPECIFIC_DT #define BCMBCA_BOARDID_MAXSIZE 28 #define BCMBCA_VOICEBOARDID_MAXSIZE BCMBCA_BOARDID_MAXSIZE #include "spl_ddrinit.h" static int on_boardid(const char *name, const char *value, enum env_op op, int flags) { char boardid[BCMBCA_BOARDID_MAXSIZE+4]; int ret = 0; int img_index = 0; long conf, node, offset; uint32_t env_mcb; if (forced_updates || ((flags & H_INTERACTIVE) == 0)) return 0; switch (op) { case env_op_create: case env_op_overwrite: img_index = get_active_img_idx(); if((img_index < MIN_IMG_INDEX) || (img_index > MAX_IMG_INDEX)) img_index = MIN_IMG_INDEX; /* Load bootfs to load address */ if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD if (nand_load_fit(img_index, load_addr)) { printf("Failed to load bootfs\n"); return 0; } #endif } if( ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { #ifdef BCA_SDK_EMMC_CMD ret = emmc_load_bootfs(img_index, load_addr); if (ret) { printf("Failed to load bootfs\n"); return 0; } #endif } conf = fdt_path_offset((void*)load_addr, FIT_IMAGES_PATH); if (conf < 0) { printf("Cannot find /images node: %ld\n", conf); return 0; } if (strlen(value) > BCMBCA_BOARDID_MAXSIZE) { printf("boardid is too long, max size is %d\n", BCMBCA_BOARDID_MAXSIZE); return 1; } sprintf(boardid,"fdt_%s", value); for (node = fdt_first_subnode((const void*)load_addr, conf); node >= 0; node = fdt_next_subnode((const void*)load_addr, node)) { const char* image_name = fit_get_name((const char*)load_addr, node, NULL); if (strcmp(image_name, boardid) == 0) { const uint32_t* memcfg; uint32_t fdt_mcb; const void* fdt; size_t size; ret = fit_image_get_data_and_size((const void*)load_addr, node, &fdt, &size); if (ret) { printf("Failed to get FDT\n"); return 0; } offset = fdt_path_offset(fdt, "/memory_controller"); if (offset < 0) { printf("Not found memory_controller node in FDT\n"); return 0; } memcfg = fdt_getprop(fdt, offset, "memcfg", NULL); if (memcfg == NULL) { printf("Can't find memcfg parameter in DTB\n"); return 0; } fdt_mcb = be32_to_cpu(*memcfg); env_mcb = env_get_hex("MCB", 0); if (env_mcb & BP_DDR_CONFIG_OVERRIDE) { printf("MCB sticky bit is set, MCB is not updated\n Current MCB 0x%x, FDT MCB 0x%x\n", env_mcb, fdt_mcb); return 0; } printf("Updating MCB environment from 0x%x to 0x%x\n", env_mcb, fdt_mcb); env_set_hex("MCB", fdt_mcb); env_set("boardid", value); printf("Memory Configuration Changed -- SAVING ENV AND REBOOT NEEDED\n"); return 0; } } printf("Error: boardid %s not supported.\nList of supported boards:\n", value); for (node = fdt_first_subnode((const void*)load_addr, conf); node >= 0; node = fdt_next_subnode((const void*)load_addr, node)) { char* name = fit_get_name((const char*)load_addr, node, NULL); if ((strncmp("fdt_", name, 4) == 0) && (strcmp("fdt_uboot", name))) printf("%s\n", (name+4)); } return 1; case env_op_delete: if (flags & H_FORCE) return 0; printf("## boardid may not be deleted\n"); return 1; default: return 0; } } U_BOOT_ENV_CALLBACK(boardid, on_boardid); static int on_voiceBoardid(const char *name, const char *value, enum env_op op, int flags) { char boardid[BCMBCA_BOARDID_MAXSIZE+4]; int ret = 0; int img_index = 0; long conf, node, offset; char *pboardid; if (forced_updates || ((flags & H_INTERACTIVE) == 0)) return 0; pboardid = env_get("boardid"); if (!pboardid) { printf("boardId must be set first.\n"); return 1; } switch (op) { case env_op_create: case env_op_overwrite: img_index = get_active_img_idx(); if((img_index < MIN_IMG_INDEX) || (img_index > MAX_IMG_INDEX)) img_index = MIN_IMG_INDEX; /* Load bootfs to load address */ if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD if (nand_load_fit(img_index, load_addr)) { printf("Failed to load bootfs\n"); return 0; } #endif } if( ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { #ifdef BCA_SDK_EMMC_CMD ret = emmc_load_bootfs(img_index, load_addr); if (ret) { printf("Failed to load bootfs\n"); return 0; } #endif } conf = fdt_path_offset((void*)load_addr, FIT_IMAGES_PATH); if (conf < 0) { printf("Cannot find /images node: %ld\n", conf); return 0; } if (strlen(value) > BCMBCA_VOICEBOARDID_MAXSIZE) { printf("voiceboardid is too long, max size is %d\n", BCMBCA_VOICEBOARDID_MAXSIZE); return 1; } sprintf(boardid,"fdt_%s", pboardid); for (node = fdt_first_subnode((const void*)load_addr, conf); node >= 0; node = fdt_next_subnode((const void*)load_addr, node)) { const char* image_name = fit_get_name((const char*)load_addr, node, NULL); if (strcmp(image_name, boardid) == 0) { size_t size; const void* fdt; int i, len, idx, slicCount; const char *sliclist; ret = fit_image_get_data_and_size((const void*)load_addr, node, &fdt, &size); if (ret) { printf("Failed to get FDT\n"); return 0; } offset = fdt_path_offset(fdt, "/bcm_voice"); if (offset < 0) { printf("Not found voice node in FDT\n"); return 0; } idx = fdt_stringlist_search(fdt, offset, "sliclist", value); /* is the string is found, save it and exit */ if (0 <= idx) { env_set("voiceboardid", value); printf("-- saving env and reboot needeD\n"); return 0; } printf("%d: %s is not found. idx=%d\n", __LINE__, value, idx); slicCount = fdt_stringlist_count(fdt, offset, "sliclist"); printf("slicCount=%d. List of supported daughter cards:\n", slicCount); for (i=0; i < slicCount; i++) { sliclist = fdt_stringlist_get(fdt, offset, "sliclist", i, &len); if (sliclist) printf("%s\n", sliclist); else printf("%d: fdt_stringlist_get failed. len=%d\n", __LINE__, len); } return 1; } } return 1; case env_op_delete: env_set("voiceboardid", ""); printf("-- Saving env and reboot needed\n"); return 0; default: return 0; } } U_BOOT_ENV_CALLBACK( voiceboardid, on_voiceBoardid); #endif static int get_active_img_idx( void ) { int node,len; unsigned int active_img_idx = 0; fdt32_t * nodep = NULL; node = fdt_path_offset(gd->fdt_blob, "/chosen"); if (node < 0) { printf("Can't find /chosen node in uboot DTB\n"); return node; } nodep = (fdt32_t *)fdt_getprop(gd->fdt_blob, node, "active_image", &len); active_img_idx = fdt32_to_cpu(*(fdt32_t *)nodep); return active_img_idx; } static int set_active_img_idx( int img_idx ) { int node,len; fdt32_t * nodep = NULL; node = fdt_path_offset(gd->fdt_blob, "/chosen"); int ret = -1; if (node < 0) { printf("Can't find /chosen node in uboot DTB, device may not boot properly!\n"); } else { ret = fdt_setprop_u32(gd->fdt_blob, node, "active_image", img_idx); if(ret) { printf("Could not set active image node in the fdt, device may not boot properly!\n"); } } return ret; } /************************************************************ * Flash Specific Functions * ************************************************************/ #ifdef BCA_SDK_EMMC_CMD static int emmc_restoredefault(void) { ulong part_size_blk, part_start_blk; char cmd[128]; int ret = -1; /* Switch to user data partition */ if ( run_command("mmc dev 0 0", 0) == 0 ) { env_set("part_start_blk", NULL); env_set("part_size_blk", NULL); run_command("part start mmc 0 data part_start_blk", 0); run_command("part size mmc 0 data part_size_blk", 0); part_start_blk = env_get_hex("part_start_blk", 0); part_size_blk = env_get_hex("part_size_blk", 0); /* If data exists, delete it */ if( part_size_blk ) { sprintf(cmd, "mmc erase 0x%lx 0x%lx", part_start_blk, part_size_blk); ret = run_command(cmd, 0); } else { ret = 0; } } else { printf("Error: Cannot switch to userdata partition!\n"); } return ret; } static int emmc_rdwr_boot_part( ulong addr, ulong size, int img_index, int write ) { char cmd[128]; ulong num_blocks = 0; ulong block_addr = 0; ulong part_size_blk = 0; int boot_part_num = 1; int ret = -1; struct mmc *mmc = NULL; mmc = find_mmc_device(0); if( !mmc ) { printf("Error: Cannot access mmc device!\n"); goto emmc_rdwr_boot_part_exit; } /* Calculate number of blocks */ num_blocks = size/mmc->read_bl_len + (size%mmc->read_bl_len?1:0); /* Determine size of raw boot partition */ part_size_blk = mmc->capacity_boot/mmc->read_bl_len; if( num_blocks > part_size_blk ) { printf("Error: Insufficient space in boot partition %d for 0x%lx blocks. Partition size is 0x%lx\n", boot_part_num, num_blocks, part_size_blk); goto emmc_rdwr_boot_part_exit; } /* Switch to boot partition */ sprintf(cmd, "mmc dev 0 %d", boot_part_num); if ( run_command(cmd, 0) ) { printf("Error: Cannot switch to bootpartition %d!\n", boot_part_num); goto emmc_rdwr_boot_part_exit; } /* Write to raw boot partition */ sprintf(cmd, "mmc %s 0x%lx 0x%lx 0x%lx", (write?"write":"read"), addr, block_addr, num_blocks); ret = run_command(cmd, 0); printf("%s: %s (bootp%d)\n", __FUNCTION__, cmd, boot_part_num); /* Switch back to userdata partition */ run_command("mmc dev 0 0", 0); emmc_rdwr_boot_part_exit: if( ret ) printf("Error: Failed %s 0x%lx blocks from/to boot partition %d!\n", (write?"writing":"reading"), num_blocks, boot_part_num); return ret; } static int emmc_rdwr_userdata_part( char * part_name, ulong addr, ulong size, int write ) { char cmd[128]; ulong num_blocks = 0; ulong block_addr = 0; ulong part_size_blk = 0; int ret = -1; struct mmc *mmc = NULL; mmc = find_mmc_device(0); if( !mmc ) { printf("Error: Cannot access mmc device!\n"); goto emmc_rdwr_usrdata_part_exit; } /* Calculate number of blocks */ num_blocks = size/mmc->read_bl_len + (size%mmc->read_bl_len?1:0); /* Switch to user data partition */ if ( run_command("mmc dev 0 0", 0) ) { printf("Error: Cannot switch to userdata partition!\n"); goto emmc_rdwr_usrdata_part_exit; } /* Get GPT partition size in blocks */ env_set("part_size_blk", NULL); sprintf(cmd, "part size mmc 0 %s part_size_blk", part_name); if(run_command(cmd, 0) == 0) { part_size_blk = env_get_hex("part_size_blk", 0); if( num_blocks > part_size_blk ) { printf("Error: Insufficient space in partition %s for 0x%lx blocks. Partition size is 0x%lx\n", part_name, num_blocks, part_size_blk); goto emmc_rdwr_usrdata_part_exit; } } else { printf("Error: Cannot determine size of GPT partition %s\n", part_name); goto emmc_rdwr_usrdata_part_exit; } /* Get start block address of partition */ sprintf(cmd, "part start mmc 0 %s part_start_blk", part_name); env_set("part_start_blk", NULL); if(run_command(cmd, 0) == 0) { block_addr = env_get_hex("part_start_blk", 0); sprintf(cmd, "mmc %s 0x%lx 0x%lx 0x%lx", (write?"write":"read"), addr, block_addr, num_blocks); ret = run_command(cmd, 0); printf("%s: %s (%s)\n", __FUNCTION__, cmd, part_name); } else { printf("Error: Cannot determine start block of GPT partition %s\n", part_name); goto emmc_rdwr_usrdata_part_exit; } emmc_rdwr_usrdata_part_exit: if( ret ) printf("Error: Failed %s 0x%lx blocks from/to GPT partition %s!\n", (write?"writing":"reading"), num_blocks, part_name); return ret; } static int flash_rootfs_emmc( ulong addr, ulong size, int img_index ) { char part_name[128]; int ret = CMD_RET_FAILURE; if( !addr || !size || img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX ) return CMD_RET_USAGE; sprintf(part_name, "rootfs%d", img_index); ret = emmc_rdwr_userdata_part( part_name, (ulong)addr, size, 1 ); return ret; } static int flash_bootfs_emmc( ulong addr, ulong size, int img_index ) { char part_name[128]; int ret = CMD_RET_FAILURE; if( !addr || !size || img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX ) return CMD_RET_USAGE; sprintf(part_name, "bootfs%d", img_index); ret = emmc_rdwr_userdata_part( part_name, (ulong)addr, size, 1 ); return ret; } static int flash_loader_emmc( ulong addr, ulong size, int img_index ) { int ret = -1; if( !addr || !size ) return CMD_RET_USAGE; ret = emmc_rdwr_boot_part( addr, size, img_index, 1); return ret; } static int set_emmc_metadata( char* metadata, int size ) { char part_name[128]; int ret; int i; for (i = 1; i < 3; i++) { sprintf(part_name, "metadata%d", i); ret = emmc_rdwr_userdata_part( part_name, (ulong)metadata, size, 1 ); } return ret; } static int get_emmc_metadata( char* metadata, int size ) { int ret; int committed = 0; int valid[2] = {0,0}; int seq[2] = {-1,-1}; int i; char part_name[128]; for (i = 1; i < 3; i++) { sprintf(part_name, "metadata%d", i); ret = emmc_rdwr_userdata_part( part_name, (ulong)metadata, size, 0 ); if (0 == validate_metadata(metadata, valid, &committed, seq)) { break; } else { printf("metadata parse error\n"); ret = CMD_RET_FAILURE; } } return ret; } static ulong emmc_get_bootfs_size( char * fit ) { int images_noffset; int noffset; int ndepth; int count; size_t size; ulong bootfs_size = 0; const void *data; /* Find images parent node offset */ images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); if (images_noffset < 0) { printf("Can't find images parent node '%s' (%s)\n", FIT_IMAGES_PATH, fdt_strerror(images_noffset)); return 0; } /* Process all image subnodes */ for (ndepth = 0, count = 0, noffset = fdt_next_node(fit, images_noffset, &ndepth); (noffset >= 0) && (ndepth > 0); noffset = fdt_next_node(fit, noffset, &ndepth)) { if (ndepth == 1) { /* * Direct child node of the images parent node, * i.e. component image node. */ count++; if (fit_image_get_data_and_size(fit, noffset, &data, &size) == 0) { bootfs_size = ((ulong)data+(ulong)size) - (ulong)fit; } } } printf("Bootfs size is %lu bytes\n", bootfs_size); return bootfs_size; } static int emmc_add_gpt_part( char * name, ulong size ) { char * current_parts = NULL; char * new_parts = NULL; int ret = -1; /* Read partitions into variable */ env_set("current_parts", NULL); run_command("gpt read mmc 0 current_parts", 0); current_parts = env_get("current_parts"); if( strstr(current_parts, name) ) { printf("%s: GPT partition:%s of size:%luMiB exists!\n", __FUNCTION__, name, size); return 0; } else { printf("%s: Adding GPT partition %s of size %luMiB\n", __FUNCTION__, name, size); } /* Update partitions */ new_parts = malloc(strlen(current_parts) + 1024 ); if( new_parts ) { sprintf(new_parts, "%sname=%s,size=%luMiB;", current_parts, name, size); ret = env_set("current_parts", new_parts); free(new_parts); ret = run_command("gpt write mmc 0 $current_parts", 0); ret = run_command("gpt verify mmc 0 $current_parts", 0); } else { printf("%s: Error allocating memory for partition string!\n", __FUNCTION__); } //env_set("current_parts", NULL); return ret; } /* This function will create GPT partitions if it detects partition sizes specfied via * uboot environment variables. The env variable name has to be in the format: * _vol_size= * The function will then create GPT partition with name of size * * NOTE: For NAND, equivalent ubi volumes are also created dynamically based on the same * uboot env variables. However, for NAND the volumes are created in the mount-fs.sh linux * startup script instead of in uboot proper. */ #define VOL_SIZE_STR "_vol_size=" static int emmc_do_gpt_fixup(void) { env_t *ep; char *envbuf = NULL; int ret = -2; int elen; char *config; char * token = NULL; char * token2 = NULL; char * name; int value; ulong token_len = 0; config = env_get("env_boot_magic"); if (NULL == config) { printf("env_boot_magic missing in the env\n"); return -1; } elen = simple_strtoul(config, NULL, 0); elen = max(elen, CONFIG_ENV_SIZE); envbuf = malloc(elen); memset(envbuf, 0, elen); if (!envbuf) { printf("memory allocation failed\n"); return -2; } ep = (env_t *) (envbuf + 8); ret = env_export(ep); /* Search environment for variable names matching _vol_size */ token = (char*)(ep->data); while(strlen(token) && (ret == 0) && ((ulong)token < ((ulong)envbuf+elen))) { token_len = strlen(token); if( (token2 = strstr(token, VOL_SIZE_STR)) ) { *token2 = '\0'; name = token; value = atoi( (char*)((ulong)token2 + strlen(VOL_SIZE_STR)) ); printf("%s: Detected env defined partition:%s, of size:%dM\n", __FUNCTION__, name, value); ret = emmc_add_gpt_part(name, value); ret = 0; } token = (char*)((ulong)token + token_len + 1); } free(envbuf); return(ret); } static int do_gpt_fixup(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { return emmc_do_gpt_fixup(); } static int emmc_load_bootfs( int img_index, uint32_t bootfs_load_addr ) { char cmd[128]; ulong size; int ret = CMD_RET_FAILURE; int fit_auth= -1; /* Set rootfs volume id */ int rootfs_partid = (img_index == 1?IMAGE_PART_ID_1:IMAGE_PART_ID_2)+1; /* Switch to userdata partition */ if( run_command("mmc dev 0 0", 0) == 0 ) { //FIXME: OR skip loading if FIT is already in memory //FIXME: IF FIT is loaded from flash, reverify RSA of header sprintf(cmd, "bootfs%d", img_index); /* Get FDT header */ size = sizeof(struct fdt_header); ret = emmc_rdwr_userdata_part( cmd, (ulong)bootfs_load_addr, size, 0 ); /* Retrieve entire FIT header */ ret = emmc_rdwr_userdata_part( cmd, (ulong)bootfs_load_addr, bcm_sec_get_reqd_load_size((ulong)bootfs_load_addr), 0 ); if (!ret) { fit_auth = bcm_sec_validate_fit((void*)bootfs_load_addr, 0x10000); } /* Determine size of bootfs from fdt_header and load it */ size = emmc_get_bootfs_size((void *)bootfs_load_addr); ret = emmc_rdwr_userdata_part( cmd, (ulong)bootfs_load_addr, size, 0 ); /* generate mapper parameters */ sprintf(cmd,"/dev/mmcblk0p%d", rootfs_partid); /* If device mapper not being used, set rootfs_opts manually */ if ( (bcm_handle_mapper((void*)(ulong)bootfs_load_addr, cmd, "")) != 0) { /* Set default bootargs */ sprintf(cmd, "env set -f rootfs_opts root=/dev/mmcblk0p%d", rootfs_partid); run_command(cmd, 0); } if (!fit_auth) { bcm_board_boot_fdt_fixup((void*)bootfs_load_addr); } } return ret; } #endif /* BCA_SDK_EMMC_CMD */ #ifdef BCA_SDK_NAND_CMD #define EXTRA_SPC_SUFFIX_STR "_extra_space" static uint64_t nand_get_part_extra_bytes(char* partition_prefix) { char var_name[128]; char * extra_space_str = NULL; uint64_t extra_bytes = 0; unsigned long iargs[4] = {0}; char units[4]= {0}; sprintf(var_name, "%s%s", partition_prefix, EXTRA_SPC_SUFFIX_STR); extra_space_str = env_get(var_name); if( extra_space_str ) { parse_env_nums(extra_space_str, 1, iargs, units); extra_bytes = ((long long)iargs[0]) << suffix2shift(units[0]); } if( extra_bytes < SIZE_OVERHEAD ) extra_bytes = SIZE_OVERHEAD; return extra_bytes; } static uint64_t nand_get_avail_space(char * update_vol_name) { int i; static struct ubi_device *ubi; static struct ubi_volume *vol; uint64_t vol_size = 0; uint64_t avail_space = 0; int ret = -1; ret = run_command("ubi part image", 0); if( ret == 0 ) { ubi = ubi_devices[0]; for (i = 0; i < (ubi->vtbl_slots + 1); i++) { if (!ubi->volumes[i]) continue; /* Empty record */ vol = ubi->volumes[i]; if( strcmp(vol->name,update_vol_name) == 0 ) { vol_size = vol->reserved_pebs * vol->usable_leb_size; break; } } avail_space = ubi->avail_pebs * ubi->leb_size + vol_size; } return avail_space; } static int nand_restoredefault(void) { int ret = -1; if(run_command("ubi part image", 0) == 0) { /* If data exists, delete it */ if(run_command("ubi check data", 0) == 0 ) { ret = run_command("ubi remove data", 0); } else { ret = 0; } } else { printf("Error: Cannot attach ubi!\n"); } return ret; } static int flash_rootfs_nand( ulong addr, ulong size, int img_index ) { char cmd[128]; int ret = CMD_RET_FAILURE; int rootfs_volid = (img_index == 1? IMAGE_VOL_ID_1:IMAGE_VOL_ID_2) + 1; uint64_t avail_bytes = 0; uint64_t extra_space_bytes = nand_get_part_extra_bytes("rootfs"); if( !addr || !size || img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX ) return CMD_RET_USAGE; if(run_command("ubi part image", 0) == 0) { /* check if new rootfs will fit in flash */ sprintf(cmd, "rootfs%d", img_index); avail_bytes = nand_get_avail_space(cmd); if( avail_bytes < size+extra_space_bytes ) { printf("Error: Insufficient space in NAND for 0x%lx Bytes . Available size is 0x%llx\n", size+extra_space_bytes, avail_bytes); return -1; } /* If rootfs1 exists, delete it */ sprintf(cmd, "ubi check rootfs%d", img_index); if(run_command(cmd, 0) == 0) { sprintf(cmd, "ubi remove rootfs%d", img_index); run_command(cmd, 0); } /* Create rootfs volume */ sprintf(cmd, "ubi create rootfs%d %llx dynamic %d", img_index, size+extra_space_bytes, rootfs_volid); ret = run_command(cmd, 0); /* Write rootfs data volume */ if( ret == 0 ) { sprintf(cmd, "ubi write %lx rootfs%d %lx\n", addr, img_index, size); ret = run_command(cmd, 0); } run_command("ubi detach", 0); } return ret; } static int flash_bootfs_nand( ulong addr, ulong size, int img_index ) { char cmd[128]; int ret = CMD_RET_FAILURE; int bootfs_volid = (img_index == 1? IMAGE_VOL_ID_1:IMAGE_VOL_ID_2); int active_img_idx; uint64_t avail_bytes = 0; uint64_t extra_space_bytes = nand_get_part_extra_bytes("bootfs"); if( !addr || !size || img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX ) return CMD_RET_USAGE; ret = run_command("ubi part image", 0); if( ret ) { /* Attach failed, check if we are running bootstrap code */ active_img_idx = get_active_img_idx(); if( active_img_idx == ACTIVE_IMGIDX_BOOTSTRAP ) { /* We are running bootstrap image, wipe Image partition and retry */ printf("UBI attach fails for bootstrap image --> erasing IMAGE MTD partition\n"); run_command("mtd erase image", 0); ret = run_command("ubi part image", 0); } } if(ret == 0) { /* If fit1 exists, delete it */ if(run_command("ubi check fit1", 0) == 0) run_command("ubi remove fit1", 0); /* check if new bootfs will fit in flash */ sprintf(cmd, "bootfs%d", img_index); avail_bytes = nand_get_avail_space(cmd); if( avail_bytes < size+extra_space_bytes ) { printf("Error: Insufficient space in NAND for 0x%lx Bytes . Available size is 0x%llx\n", size+extra_space_bytes, avail_bytes); return -1; } /* If bootfs exists, delete it */ sprintf(cmd, "ubi check bootfs%d", img_index); if(run_command(cmd, 0) == 0) { sprintf(cmd, "ubi remove bootfs%d", img_index); run_command(cmd, 0); } /* Create bootfs volume */ sprintf(cmd, "ubi create bootfs%d %llx static %d", img_index, size+extra_space_bytes, bootfs_volid); ret = run_command(cmd, 0); /* Write img data */ if( ret == 0 ) { sprintf(cmd, "ubi write %lx bootfs%d %lx\n", addr, img_index, size); ret = run_command(cmd, 0); } run_command("ubi detach", 0); } else { printf("Error! UBI attach failed!\n"); } return ret; } static int flash_loader_nand( ulong addr, ulong size, int img_index ) { struct mtd_info * mtd; struct erase_info erase_op = {}; loff_t off; int ret; size_t retlen; ulong remaining_bytes = size; mtd = get_mtd_device_nm("loader"); if( !addr || !size ) return CMD_RET_USAGE; if( !mtd ) { printf("ERROR: Failed to retrieve MTD device for NAND loader!\n"); return -1; } if( size > mtd->size ) { printf("ERROR: loader size 0x%08llx is greater than mtd partition size 0x%08llx!\n", size, mtd->size); put_mtd_device(mtd); return -1; } (void)img_index; /* Block by block write of loader */ for( off=0; off < mtd->size; off += mtd->erasesize ) { if( remaining_bytes ) { /* Erase block */ erase_op.mtd = mtd; erase_op.addr = off; erase_op.len = mtd->erasesize; erase_op.scrub = 0; printf("Erasing next block at 0x%08llx\n", off); ret = mtd_erase(mtd, &erase_op); /* Abort if its not a bad block error */ if ((ret == -EIO) && erase_op.fail_addr) { printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr); /* Truncate image size if we cant fit due to bad block skip */ if( remaining_bytes > (mtd->size - off - mtd->erasesize) ) remaining_bytes -= (remaining_bytes < mtd->erasesize?remaining_bytes:mtd->erasesize); continue; } else if ( ret ) { printf("ERROR: Failed to erase NAND block 0x%08llx\n", off); break; } /* write block */ printf("Writing next block at 0x%08llx\n", off); ret = mtd_write(mtd, off, (remaining_bytes < mtd->erasesize?remaining_bytes:mtd->erasesize), &retlen, (const uchar*)(addr)); if( ret ) { printf("ERROR: Failed to write NAND block 0x%08llx\n", off); break; } addr += (remaining_bytes < mtd->erasesize?remaining_bytes:mtd->erasesize); remaining_bytes -= (remaining_bytes < mtd->erasesize?remaining_bytes:mtd->erasesize); } else break; } if( ret ) { /* Loader upgrade failed for some reason */ printf("ERROR: Failed to flash NAND loader! re=%d\n", ret); } else { /* Loader upgrade worked */ printf("NAND loader flashed successfully!\n"); } put_mtd_device(mtd); return ret; } static int set_nand_metadata( char* metadata, int size ) { char cmd[128]; int ret; int i; int volmap[] = {-1,METADATA_VOL_ID_1,METADATA_VOL_ID_2}; ret = ubi_part("image", NULL); if (ret != 0) { return (ret); } for (i = 1; i < 3; i++) { sprintf(cmd, "ubi remove metadata%d", i); run_command(cmd, 0); sprintf(cmd, "ubi create metadata%d %d static %d", i, METADATA_SIZE + 1024, volmap[i]); run_command(cmd, 0); sprintf(cmd, "metadata%d", i); ubi_volume_write(cmd, metadata, size); } run_command("ubi detach", 0); return ret; } static int get_nand_metadata( char* metadata, int size ) { int ret; int committed = 0; int valid[2] = {0,0}; int seq[2] = {-1,-1}; int i; char name[128]; ret = ubi_part("image", NULL); if (ret != 0) { return (ret); } for (i = 1; i < 3; i++) { sprintf(name, "metadata%d", i); ret = ubi_volume_read(name, metadata, size); printf("read from %s returned %d\n", name, ret); if (0 == validate_metadata(metadata, valid, &committed, seq)) { break; } else { printf("metadata parse error\n"); ret = CMD_RET_FAILURE; } } run_command("ubi detach", 0); return ret; } static int nand_load_fit( int img_index, uint32_t fit_load_addr ) { char cmd[128]; int ret = CMD_RET_FAILURE; int fit_auth= -1; if(run_command("ubi part image", 0) == 0) { //FIXME: OR skip loading if FIT is already in memory //FIXME: IF FIT is loaded from flash, reverify RSA of header sprintf(cmd, "ubi read %lx bootfs%d", fit_load_addr, img_index); ret = run_command(cmd, 0); /* may be it is just bootstrap image */ if (ret) { sprintf(cmd, "ubi read %lx fit1", fit_load_addr); ret = run_command(cmd, 0); } } return ret; } static unsigned int bcm_handle_mapper(void* fit, char *flash_device, char *flash_opts) { const char *val; char cmd[768]; char work[768]; char *cp; int off, len; char dmdev[32]; unsigned int magic; off = fdt_path_offset(fit, "/brcm_rootfs_encrypt"); if (off < 0) { return(1); } strcpy(dmdev,"/dev/dm-0"); if (val = fdt_getprop(fit, off, "dev", &len)) { strncpy(dmdev, val, len); dmdev[len] = 0; } val = fdt_getprop(fit, off, "type", &len); if (val == NULL) { return(1); } val = fdt_getprop(fit, off, "mapper", &len); if (val == NULL) { return(1); } strncpy(work, val, len); work[len] = 0; while (cp = strstr(work,"%DEVICE%")) { strncpy(cmd, work, cp-work); sprintf(&cmd[cp-work], "%s %s", \ flash_device, cp+8); strcpy(work,cmd); } if (cp = strstr(work,"%IMAGE_KEY%")) { int i; val = NULL; len = 0; off = fdt_path_offset(gd->fdt_blob, "/trust/key_image_aes"); if (off < 0) { printf("ERROR: Can't find /trust/key_image_aes node in boot DTB!\n"); } val = (char*)(fdt_getprop(gd->fdt_blob, off, "value", &len)); strncpy(cmd, work, cp-work); for (i = 0 ; i < len ; i++) { sprintf(&cmd[cp-work+2*i], "%02x", val[i]); } sprintf(&cmd[cp-work+2*i], "%s", cp+11); strcpy(work,cmd); } sprintf(cmd, "root=%s %s dm-mod.create=\"%s\"", dmdev, flash_opts, work); env_set("rootfs_opts", cmd); return(0); } static int nand_load_bootfs( int img_index, uint32_t bootfs_load_addr ) { char cmd[128]; char device[32]; unsigned int magic = 0; int ret = CMD_RET_FAILURE; int fit_auth= -1; /* Set rootfs volume id */ int rootfs_volid = (img_index == 1?IMAGE_VOL_ID_1:IMAGE_VOL_ID_2)+1; if(run_command("ubi part image", 0) == 0) { /* Set default bootargs */ /* Determine rootfs type */ sprintf(cmd, "ubi read %lx bootfs%d", bootfs_load_addr, img_index); ret = run_command(cmd, 0); if (!ret) { fit_auth = bcm_sec_validate_fit((void*)bootfs_load_addr, 0x10000); } sprintf(device,"/dev/ubiblock0_%d", rootfs_volid); sprintf(cmd,"ubi.mtd=1 ubi.block=0,%d rootfstype=squashfs", rootfs_volid); if (bcm_handle_mapper((void*)(ulong)bootfs_load_addr, device, cmd) == 0) { /* mapper has set up the whole thing */ if (!fit_auth) { bcm_board_boot_fdt_fixup((void*)bootfs_load_addr); } return(0); } sprintf(cmd, "ubi read %lx rootfs%d %d", &magic, img_index, (int)sizeof(unsigned int)); if (run_command(cmd, 0) == 0) { if( magic == UBIFS_MAGIC ) { sprintf(cmd, "env set -f rootfs_opts root=ubi:rootfs%d ubi.mtd=1 rootfstype=ubifs", img_index); run_command(cmd, 0); } else if ( magic == SQUASHFS_MAGIC ) { sprintf(cmd, "env set -f rootfs_opts root=/dev/ubiblock0_%d ubi.mtd=1 ubi.block=0,%d rootfstype=squashfs", rootfs_volid, rootfs_volid); run_command(cmd, 0); } else { printf("ERROR: Invalid rootfs detected in volume rootfs1! Boot aborted!\n"); return ret; } } else { printf("ERROR: Cannot determine rootfs type! Boot aborted!\n"); return ret; } if (!fit_auth && magic == SQUASHFS_MAGIC ) { bcm_board_boot_fdt_fixup((void*)bootfs_load_addr); } } return ret; } #endif /* BCA_SDK_NAND_CMD */ #if defined(BCA_SDK_SPINOR_CMD) static int spinor_load_bootfs(uint32_t bootfs_load_addr) { int ret= -1; struct mtd_info *mtd; size_t retlen; int fit_auth; mtd_probe_devices(); mtd = get_mtd_device_nm(BOOTFS_PART); if (IS_ERR_OR_NULL(mtd)){ debug("%s:MTD device %s not found, ret %ld\n",__func__, BOOTFS_PART, PTR_ERR(mtd)); return ret; } ret = mtd_read(mtd,0,mtd->size,&retlen,(u_char*)bootfs_load_addr ); if (!ret) { fit_auth = bcm_sec_validate_fit((void*)bootfs_load_addr, 0x10000); } /* If device mapper not being used, set the rootfs_opts manually */ if (bcm_handle_mapper((void*)(ulong)bootfs_load_addr, "/dev/mtdblock3", "rootfstype=squashfs") != 0) { env_set("rootfs_opts","root=/dev/mtdblock3 rootfstype=squashfs"); } if (!fit_auth) { bcm_board_boot_fdt_fixup((void*)bootfs_load_addr); } put_mtd_device(mtd); return ret; } static int spinor_restoredefault(void) { struct mtd_info *mtd; struct erase_info ei; int ret; mtd_probe_devices(); mtd = get_mtd_device_nm(DATA_PART); if (IS_ERR_OR_NULL(mtd)){ printf("ERROR!!:failed to get data partition!!\n"); return CMD_RET_FAILURE; } memset(&ei, 0, sizeof(ei)); ei.mtd = mtd; ei.addr = 0; ei.len = mtd->size; ret = mtd_erase(mtd, &ei); if (ret){ printf("ERROR!!: failed to restore to default!!\n"); ret = CMD_RET_FAILURE; } else printf("Restore to default done.\n"); put_mtd_device(mtd); return ret; } static int flash_loader_spinor( ulong addr, ulong size) { int ret= -1; struct mtd_info *mtd; struct erase_info ei; size_t retlen; char *config; u_char *envbuf = NULL; char *found = NULL; int elen; int i; size_t wr_len; char *c; if( !addr || !size ) return CMD_RET_USAGE; mtd_probe_devices(); mtd = get_mtd_device_nm(LOADER_PART); if (IS_ERR_OR_NULL(mtd)){ printf("%s:MTD device %s not found, ret %ld\n",__func__, LOADER_PART, PTR_ERR(mtd)); return ret; } config = env_get("env_boot_magic"); if (NULL != config){ elen = simple_strtoul(config, NULL, 0); found = malloc(strlen(config) + 1); if (found){ strcpy(found, config); envbuf = malloc(elen + 12); if(envbuf){ strtok(found, "@"); while ((c = strtok(NULL, ","))) { i = simple_strtoul(c, NULL, 0); debug("read from %x\n", i); wr_len = elen+12; debug("read len %x\n", wr_len); if( mtd_read(mtd,i,wr_len,&retlen, envbuf)){ debug("%s:MTD device %s read fail\n",__func__, LOADER_PART); continue; } /*assume addr + i will not cause any issue*/ memcpy(addr+i, envbuf, wr_len); /*need this?*/ if( size < (i + wr_len)){ debug("%s:Change original size from %ld to %d \n",__func__,size, i + wr_len); size = i + wr_len; } } free(envbuf); } free(found); } } printf("\nErasing MTD partition %s...\n",LOADER_PART); memset(&ei, 0, sizeof(ei)); ei.mtd = mtd; ei.addr = 0; ei.len = mtd->size; ret = mtd_erase(mtd, &ei); if (ret){ debug("%s:erase MTD device %s fail, ret %d\n",__func__, LOADER_PART, ret); goto done; } printf("Writing MTD partition %s...\n",LOADER_PART); ret = mtd_write(mtd, 0, size, &retlen, addr); if (ret){ debug("%s:write MTD device %s fail, ret %d\n",__func__, LOADER_PART, ret); } debug("%s:Erase/write MTD device %s done, ret %d\n",__func__, LOADER_PART, ret); done: put_mtd_device(mtd); return ret; } static int write_spinor_partition( char *const partitionname, ulong addr, ulong size ) { int ret= -1; struct mtd_info *mtd; struct erase_info ei; size_t retlen; if( !addr || !size ) return CMD_RET_USAGE; mtd_probe_devices(); mtd = get_mtd_device_nm(partitionname); if (IS_ERR_OR_NULL(mtd)){ debug("%s:MTD device %s not found, ret %d\n",__func__, partitionname, PTR_ERR(mtd)); return ret; } printf("\nErasing MTD partition %s...\n",partitionname); memset(&ei, 0, sizeof(ei)); ei.mtd = mtd; ei.addr = 0; ei.len = mtd->size; ret = mtd_erase(mtd, &ei); if (ret){ debug("%s:erase MTD device %s fail, ret %d\n",__func__, partitionname, ret); goto done; } printf("Writing MTD partition %s...\n",partitionname); ret = mtd_write(mtd, 0, size, &retlen, (u_char*)addr); if (ret){ debug("%s:write MTD device %s fail, ret %d\n",__func__, partitionname, ret); } done: put_mtd_device(mtd); return ret; } static int do_flash_spinor_binary(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { char cmd[128]; ulong size; ulong addr; /* Download binary image */ sprintf(cmd, "tftp %lx %s", load_addr, argv[1]); if( run_command(cmd, 0) == 0 ) { addr = env_get_hex("fileaddr", 0); size = env_get_hex("filesize", 0); write_spinor_partition(SPIFLASH_MTDNAME, addr, size); } else printf("ERROR!!: Failed to tftp spinor binary!!\n"); return 0; } static int do_flash_spinor_bootfs_rootfs(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { char filename[30]; int flash_fit=0; int ret = CMD_RET_FAILURE; char cmd[128]; ulong size; ulong addr; /* Parse arguments */ if ( strcmp(argv[0], "flash_spinor_fit") == 0) { flash_fit=1; sprintf(filename, "brcm_full_linux.itb"); } else { sprintf(filename, "rootfs.squashfs"); } sprintf(cmd, "tftp %lx %s\n", load_addr, filename); if( run_command(cmd, 0) == 0 ) { size = env_get_hex("filesize", 0); addr = env_get_hex("fileaddr", 0); if(flash_fit){ ret = write_spinor_partition(BOOTFS_PART, addr, size); } else{ ret = write_spinor_partition(ROOTFS_PART, addr, size); } } else printf("ERROR!!: Failed to tftp rootfs binary!!\n"); return ret; } #endif /*BCA_SDK_SPINOR_CMD*/ /************************************************************ * Generic Functions * ************************************************************/ static int sync_update_loader_bin_env(ulong loader_addr) { env_t *ep; uint32_t calc_crc; char *envbuf = NULL; uint32_t *envintp; int ret = -2; int i; loff_t off; size_t rdlen; uint32_t magichdr[3]; int num_env_found = 0; char * config; /* Search loader in memory for env headers */ for (off = 0; off < (loff_t)env_boot_magic_search_size(); off += 4096) { rdlen = 12; memcpy((char*)magichdr, (char*)loader_addr + off, rdlen); if (magichdr[0] != BOOT_MAGIC_MAGIC) { continue; } else { /* Verify CRC */ rdlen = magichdr[1]; ep = (env_t *) ((char*)loader_addr + off + 8); calc_crc = the_env_crc32(0, ep->data, rdlen - 4); if( ep->crc == calc_crc ) { num_env_found++; } else { continue; } } /* Create synced env blob */ if(num_env_found == 1) { /* Export environment to embed in new loader */ envbuf = malloc(max(rdlen + 12, CONFIG_ENV_SIZE + 12)); if( !envbuf ) { printf("ERROR: Failed to allocate mem for env!\n"); ret = -1; break; } ep = (env_t *) (envbuf + 8); envintp = (uint32_t *) envbuf; /* Delete boot magic string before exporting env */ config = env_get("env_boot_magic"); env_set("env_boot_magic", NULL); ret = env_export(ep); if( ret ) { printf("ERROR: Failed to export env!\n"); env_set("env_boot_magic", config); break; } for (i = CONFIG_ENV_SIZE; i < rdlen; i++) { envbuf[12 + i] = 0xff; } calc_crc = the_env_crc32(0, ep->data, rdlen - 4); memcpy(&ep->crc, &calc_crc, sizeof(calc_crc)); envintp[0] = BOOT_MAGIC_MAGIC; envintp[1] = rdlen; } /* Write synched env back to loader binary */ printf("Updating env in loader bin at 0x%lx\n", (long)off); memcpy((char*)loader_addr + off, envbuf, rdlen+12); } if(envbuf) free(envbuf); if(!num_env_found) ret = -1; return ret; } char * get_loader_media(void) { int node,len; char *media = NULL; if(forced_updates && strlen(forced_boot_media)) { media = forced_boot_media; printf("WARNING: forced_updates == 1, forcing boot media to %s!\n", media); } else { node = fdt_path_offset(gd->fdt_blob, "/chosen"); if (node < 0) { printf("ERROR: Can't find /chosen node in cboot DTB! Cannot determine boot media!\n"); return media; } media = (char*)(fdt_getprop(gd->fdt_blob, node, "boot_device", &len)); } return media; } static char * get_image_media(void) { char *cp = NULL; char *media = NULL; if(forced_updates && strlen(forced_image_media)) { media = forced_image_media; printf("WARNING: forced_updates == 1, forcing image media to %s!\n", media); } else { cp = env_get("IMAGE"); if (NULL != cp) { unsigned long iargs[4]; char units[4]; parse_env_string_plus_nums(cp, &media, 4, iargs, units); } else printf("ERROR: Can't find IMAGE env parameter! Cannot determine image media!\n"); } return media; } static int flash_rootfs( ulong addr, ulong size , int img_index) { int ret = -1; if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD ret = flash_rootfs_nand(addr, size, img_index); #endif } if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { #ifdef BCA_SDK_EMMC_CMD ret = flash_rootfs_emmc(addr, size, img_index); #endif } if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) { #ifdef BCA_SDK_SPINOR_CMD ret = write_spinor_partition(ROOTFS_PART, addr, size); #endif } if( ret ) printf("ERROR: Failed to flash bootfs binary!\n"); return ret; } static int flash_bootfs( ulong addr, ulong size , int img_index) { int ret = -1; if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD ret = flash_bootfs_nand(addr, size, img_index); #endif } if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { #ifdef BCA_SDK_EMMC_CMD ret = flash_bootfs_emmc(addr, size, img_index); #endif } if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) { #ifdef BCA_SDK_SPINOR_CMD ret = write_spinor_partition(BOOTFS_PART, addr, size); #endif } if( ret ) printf("ERROR: Failed to flash bootfs binary!\n"); return ret; } static int flash_loader( ulong addr, ulong size , int img_index) { int ret = -1; char * config; /* Synch uboot env to loader binary */ if( disable_runtime_env_sync ) { printf("WARNING: Not synching runtime env to loader board_spl_fit_pre_load!\n"); } else { if( sync_update_loader_bin_env(addr) ) { printf("ERROR: Could not sync runtime env to loader bin!\n"); return ret; } } if( strcasecmp(get_loader_media(), FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD ret = flash_loader_nand(addr, size, img_index); #endif } if( ret && ( strcasecmp(get_loader_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { #ifdef BCA_SDK_EMMC_CMD ret = flash_loader_emmc(addr, size, img_index); #endif } if( ret && ( strcasecmp(get_loader_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) { #ifdef BCA_SDK_SPINOR_CMD ret = flash_loader_spinor(addr, size); #endif } if( disable_runtime_env_sync ) { printf("WARNING: Not updating runtime env from flashed loader!\n"); disable_runtime_env_sync = 0; } else { /* Since sync was successful, we need to scan loader * for boot magic and update runtime env */ env_relocate(); env_set("fileaddr", NULL); env_save(); } if( ret ) printf("ERROR: Failed to flash loader binary!\n"); return ret; } static int set_metadata_val( int * committed, int * valid, int * seq ) { char* b; uint32_t *d, crc; env_t * ep; int i; int ret = -1; b = malloc(METADATA_SIZE + 2048); d = (uint32_t *) b; ep = (env_t *) & d[2]; d[0] = METADATA_SIZE; d[1] = METADATA_SIZE; i = sprintf((char*)ep->data, "COMMITTED=%d",*committed) + 1; i = i + sprintf((char*)&ep->data[i], "VALID=%d,%d",valid[0],valid[1]) + 1; i = i + sprintf((char*)&ep->data[i], "SEQ=%d,%d",seq[0],seq[1]) + 1; ep->data[i] = '\0'; crc = the_env_crc32(0, ep->data, (d[0] - 4) & 0xffff); /* * We can't use plain crc32 because someone redefines it?? * crc = crc32(0, ep->data, (d[0] - 4) & 0xffff); */ memcpy(&ep->crc, &crc, sizeof(crc)); /* Flash metadata */ if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD ret = set_nand_metadata(b, METADATA_SIZE + 16); #endif } if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { #ifdef BCA_SDK_EMMC_CMD ret = set_emmc_metadata(b, METADATA_SIZE + 16); #endif } printf("Setting committed %d valid %d,%d seq %d,%d\n", *committed, valid[0], valid[1], seq[0], seq[1]); free(b); return ret; } static int get_metadata_val( int * committed, int * valid, int * seq ) { char *b = malloc(MAX_METADATA_SIZE); int ret = -1; /* Read metadata from flash */ if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD ret = get_nand_metadata( b, MAX_METADATA_SIZE); #endif } if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { #ifdef BCA_SDK_EMMC_CMD ret = get_emmc_metadata(b, MAX_METADATA_SIZE); #endif } /* Validate metadata */ ret = validate_metadata(b, valid, committed, seq); free(b); printf("Getting committed %d valid %d,%d seq %d,%d\n", *committed, valid[0], valid[1], seq[0], seq[1]); return ret; } int commit_image( int img_index ) { int committed = 0; int valid[2] = {0,0}; int seq[2] = {-1,-1}; /*Return directly for SPI Nor*/ if( strcmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0) return 0; /* Get committed image */ get_metadata_val(&committed, valid, seq); /* update committed image */ committed = img_index; valid[img_index-1] = img_index; seq[img_index-1] = (seq[(img_index == 1) ? 1 : 0] + 1) % 1000; // set newly written image sequence number one greater than other image /* Set committed image */ set_metadata_val(&committed, valid, seq); return 0; } int get_img_index_for_upgrade(int flag) { int committed = 0; int valid[2] = {0,0}; int seq[2] = {-1,-1}; (void) flag; int img_index = get_active_img_idx(); /*Return 1 for SPI Nor*/ if( strcmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 ) return 1; /* If we know the active image, always write to the inactive image */ if( img_index ) { return( (img_index==1)?2:1 ); } /* If we cannot determine active image, then get non committed image */ get_metadata_val(&committed, valid, seq); if( !committed ) { /* If no commited images then we will flash to the 1st img index */ return 1; } else { /* If we have a committed image, then we will upgrade the uncommitted one */ return (committed == 1? 2: 1); } } #ifndef CONFIG_LOAD_FIT_OFFSET #define CONFIG_LOAD_FIT_OFFSET SZ_16M #endif static int load_linux_img( int flag, int argc, char *const argv[]) { char cmd[128]; char * board_id = NULL; int img_index = 0; uint32_t bootfs_load_addr = load_addr + CONFIG_LOAD_FIT_OFFSET; int ret = -1; if( argc == 1 ) { img_index = get_active_img_idx(); } else if( argc > 1 ) { if( strlen(argv[1]) == 1 ) img_index = *argv[1] - '0'; else { board_id = argv[1]; img_index = get_active_img_idx(); } if( argc > 2 ) { board_id = argv[2]; } } if( img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX ) { printf("ERROR: Invalid Image Index specified!\n"); return CMD_RET_FAILURE; } #if defined(CONFIG_BCM_BOOTSTATE) /* bcmbca_set_boot_reason(BCM_BOOT_REASON_WATCHDOG | BCM_BOOT_PHASE_LINUX_START | (bcmbca_get_boot_reason() & 0xffff0000) ); */ #endif /* Load bootfs to load address */ if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD ret = nand_load_bootfs(img_index, bootfs_load_addr); #endif } if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { #ifdef BCA_SDK_EMMC_CMD ret = emmc_load_bootfs(img_index, bootfs_load_addr); ret = emmc_do_gpt_fixup(); #endif } if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) { #ifdef BCA_SDK_SPINOR_CMD ret = spinor_load_bootfs(bootfs_load_addr); #endif } if( ret == 0 ) { #if defined(CONFIG_WDT) if (gd->watchdog_dev) wdt_stop(gd->watchdog_dev); #endif if(!board_id) board_id = env_get("boardid"); /* Load binaries */ sprintf(cmd, "/configurations/conf_lx_%s", board_id); if(board_id && (fdt_path_offset((void *)bootfs_load_addr, cmd) >= 0)) sprintf(cmd, "bootm start %lx#conf_lx_%s; bootm loados; bootm prep;", bootfs_load_addr, board_id); else sprintf(cmd, "bootm start %lx#conf_linux; bootm loados; bootm prep;", bootfs_load_addr); run_command(cmd, 0); } else { printf("ERROR: Failed to load bootfs%d!\n", img_index); } return ret; } static int do_load(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { if( load_linux_img( flag, argc, argv ) == 0 ) { printf("\nAll Image components have been loaded to DDR:\n"); printf(" - To edit FDT: 'fdt ..'\n"); printf(" - To launch Linux: 'bootm go'\n"); } else printf("Linux image loading Failed!\n"); return 0; } static int do_boot(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { if( load_linux_img( flag, argc, argv ) == 0 ) run_command("bootm go\n", 0); else printf("Linux image booting Failed!\n"); return 0; } static int do_flash_loader(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { char cmd[128]; const char *chip_num; ulong size; ulong addr; /* Determine chip number */ chip_num = strstr(CONFIG_SYS_SOC, "bcm"); chip_num += strlen("bcm"); if( strcasecmp(get_loader_media(), FLASH_DEV_STR_NAND) == 0 ) { sprintf(cmd, "tftp %lx loader_test_nand_%s.bin\n", load_addr, chip_num); } if( ( strcasecmp(get_loader_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { sprintf(cmd, "tftp %lx loader_test_emmc_%s.bin\n", load_addr, chip_num); } if( ( strcasecmp(get_loader_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) { sprintf(cmd, "tftp %lx loader_test_spinor_%s.bin\n", load_addr, chip_num); } if( run_command(cmd, 0) == 0 ) { size = env_get_hex("filesize", 0); addr = env_get_hex("fileaddr", 0); flash_loader(addr, size, 0); } else printf("ERROR!!: Failed to tftp loader binary!!\n"); return 0; } static int do_flash_bootfs_rootfs( char * bootfs_filename, char * rootfs_filename, int img_index ) { char cmd[128]; ulong size; ulong addr; int ret = CMD_RET_FAILURE; /* Write bootfs */ if(bootfs_filename) { sprintf(cmd, "tftp %lx %s", load_addr, bootfs_filename); if( run_command(cmd, 0) == 0 ) { size = env_get_hex("filesize", 0); addr = env_get_hex("fileaddr", 0); ret = flash_bootfs(addr, size, img_index); } else printf("ERROR!!: Failed to tftp FIT image!!\n"); } /* Write rootfs */ if(rootfs_filename) { sprintf(cmd, "tftp %lx %s\n", load_addr, rootfs_filename); if( run_command(cmd, 0) == 0 ) { size = env_get_hex("filesize", 0); addr = env_get_hex("fileaddr", 0); ret = flash_rootfs(addr, size, img_index); } else printf("ERROR!!: Failed to tftp rootfs binary!!\n"); } return ret; } static int do_flash_bins(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { char * rootfs_fname = NULL; char * bootfs_fname = NULL; int img_index = 0; int ret = CMD_RET_FAILURE; /* Parse arguments */ if ( (strcmp(argv[0], "flash_bootfs_raw") == 0) ) { bootfs_fname = argv[1]; } if( (strcmp(argv[0], "flash_rootfs_raw") == 0) ) { rootfs_fname = argv[1]; } if( argc >= 2 ) { if( (argc == 3) && (strlen(argv[2]) == 1) ) img_index = *argv[2] - '0'; else img_index = get_img_index_for_upgrade(0); } else { return CMD_RET_USAGE; } ret = do_flash_bootfs_rootfs(bootfs_fname, rootfs_fname, img_index); return ret; } static int verify_compat_string( const char * compat_str ) { const char *chip_num; char * flash_type; char * token = NULL; #ifdef BCA_SDK_NAND_CMD char flash_type_full[20]; struct mtd_info *mtd = NULL; #endif /* Early return if forced updates are enabled */ if( forced_updates ) { printf("WARNING: Skipping all compatibility checks due to forced updates being enabled!\n"); return 0; } /* Determine chip number */ chip_num = strstr(CONFIG_SYS_SOC, "bcm"); chip_num += strlen("bcm"); /* Determine flash type matches*/ flash_type = get_image_media(); if( strcmp(flash_type, FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD mtd = get_nand_dev_by_index(0); if (!mtd) { printf("ERROR: Cannot determine NAND erase block size!\n"); return CMD_RET_FAILURE; } sprintf(flash_type_full, "%s%d", flash_type, mtd->erasesize >> 10 ); #endif } /* Check compat string */ token = strtok((char *)compat_str,";"); while( token ) { if( strstr(token, "chip=")) { token += strlen("chip="); if( strcasecmp(token, chip_num) ) { printf("ERROR: Img bundle is for %s, current chip is %s\n", token, chip_num); return -1; } } else if( strstr(token, "flash=")) { token += strlen("flash="); if( strcasecmp(token, flash_type) ) { if( strcmp(flash_type, FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD if( strcasecmp(token, flash_type_full) ) { printf("ERROR: Img bundle is for %s, current flash is %s\n", token, flash_type_full); return -1; } #endif } else { printf("ERROR: Img bundle is for %s, current flash is %s\n", token, flash_type); return -1; } } } token = strtok(NULL,";"); } return 0; } static int get_binary_from_bundle( ulong bundle_addr, char * conf_name, char * name, char ** bin_name, ulong * addr, ulong * size ) { char path[128]; int conf_nodeoffset = 0; int nodeoffset = 0; int len; int ret = -1; if ( !conf_name || !name || !bin_name ) { printf("ERROR: Invalid conf_name %p, bin_name %p, name %p\n", conf_name, bin_name, name); return ret; } /* retrieve configuration node */ sprintf(path, "/configurations"); sprintf(path, "%s/%s", path, conf_name); conf_nodeoffset = fdt_path_offset((void *)bundle_addr, path); if( conf_nodeoffset < 0 ) { printf("ERROR: %s node not found in bundle!\n", conf_name); return ret; } /* Get actual name of fit node for bundle component */ *bin_name = fdt_getprop( (void *)bundle_addr, conf_nodeoffset, name, &len); if( !*bin_name ) { printf("INFO: %s not found in configuration %s ...skipping!\n", name, conf_name); return ret; } /* Retrieve node offset for bundle component */ sprintf(path, "/images/%s", *bin_name); nodeoffset = fdt_path_offset((void *)bundle_addr, path); if( nodeoffset < 0 ) { printf("ERROR: %s node not found in bundle!\n", path); return ret; } /* Get location and size of binary's data */ fit_image_get_data_and_size((void *)bundle_addr, nodeoffset, (const void**)addr, size); if( !*size ) { printf("ERROR: %s data not found in bundle!\n", *bin_name); return ret; } return 0; } static int update_flash_parts_from_loader_bin( ulong loader_addr, ulong loader_size ) { env_t *ep; uint32_t calc_crc; char *envbuf = NULL; uint32_t *envintp; int ret = -2; int i; loff_t off; size_t rdlen; uint32_t magichdr[3]; char cmd[128]; /* Search loader in memory for env headers */ for (off = 0; off < (loff_t)env_boot_magic_search_size(); off += 4096) { /* Search loader bin for environment */ rdlen = 12; memcpy((char*)magichdr, (char*)loader_addr + off, rdlen); if (magichdr[0] != BOOT_MAGIC_MAGIC) { continue; } else { /* Verify CRC */ rdlen = magichdr[1]; ep = (env_t *) ((char*)loader_addr + off + 8); calc_crc = the_env_crc32(0, ep->data, rdlen - 4); if( ep->crc == calc_crc ) { break; } else { continue; } } } /* Set runtime env variables based on loader's env */ if( off < (loff_t)env_boot_magic_search_size() ) { /* read IMAGE variable */ sprintf(cmd, "env import %lx - IMAGE", (char*)ep); run_command(cmd, 0); /* Verify */ if( env_get("IMAGE") == NULL ) { printf("ERROR! Failed to import IMAGE parameter from loader!\n"); return ret; } /* read default_partitions */ if( ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { sprintf(cmd, "env import %lx - default_partitions", (char*)ep); run_command(cmd, 0); /* Verify */ if( env_get("default_partitions") == NULL ) { printf("ERROR! Failed to import default_partitions parameter from loader!\n"); return ret; } } /* Call partition init code */ printf("INFO: flash partition config updated. Repartitioning flash!\n"); ret = board_init_flash_parts(1); } else { printf("ERROR!: No environment found in loader binary!\n"); } return ret; } int flash_upgrade_img_bundle( ulong bundle_addr , int img_index, const char * conf_name) { char path[128]; ulong addr=0; ulong size=0; ulong loader_addr=0; ulong loader_size=0; int i=0; int err = 0; const char * bin_name; const char * loader_bin_name; const char * compat_str; char *compat_copy; int nodeoffset = 0; int conf_nodeoffset = 0; int max_entries = sizeof(fn_table)/sizeof(flashfn_table_entry); int len; /* verify hashes of all upgrade bundle contents */ if( fit_all_image_verify((void *)bundle_addr) ) { /* Get offset of config node */ sprintf(path, "/configurations"); nodeoffset = fdt_path_offset((void *)bundle_addr, path); if( nodeoffset < 0 ) { printf("ERROR: %s node not found in bundle!\n", path); return CMD_RET_FAILURE; } /* If configuration is specifed then use it */ if( !conf_name ) { /* Get name of the default configuration */ conf_name = fdt_getprop((void *)bundle_addr, nodeoffset, "default", &len); if( !conf_name ) { printf("ERROR: Default configuration not found in bundle!\n"); return CMD_RET_FAILURE; } } /* Get offset of selected configurations fit node */ sprintf(path, "%s/%s", path, conf_name); conf_nodeoffset = fdt_path_offset((void *)bundle_addr, path); if( conf_nodeoffset < 0 ) { printf("ERROR: %s node not found in bundle!\n", conf_name); return CMD_RET_FAILURE; } /* Verify compatibility */ compat_str = fdt_getprop( (void *)bundle_addr, conf_nodeoffset, "compatible", &len); if( !compat_str ) { printf("Error: 'compatible' node not found in bundle!\n"); return CMD_RET_FAILURE; } compat_copy = malloc(strlen(compat_str)+1); if(!compat_copy) { printf("Error allocating memory for compatibility string!\n"); return CMD_RET_FAILURE; } strcpy(compat_copy, compat_str); if( verify_compat_string(compat_copy) ) { printf("Error: Bundle is not compatible with platform!\n"); free(compat_copy); return CMD_RET_FAILURE; } free(compat_copy); /* Decide whether we want to reformat partitions based on loader * binaries partition configuration configuration */ if( NULL == env_get("IMAGE") ) { printf("WARNING: IMAGE env variable missing! Using IMAGE from loader binary!\n"); if( get_binary_from_bundle(bundle_addr, conf_name, "loader", &loader_bin_name, &loader_addr, &loader_size) == 0 ) { printf("INFO: Reformatting flash media based on new loader binary\n"); if( update_flash_parts_from_loader_bin( loader_addr, loader_size ) ) { printf("ERROR: Failed to reformat flash based loader binary!\n"); return CMD_RET_FAILURE; } /* Set flag to prevent sync of run-time env to loader bin */ disable_runtime_env_sync = 1; } else { printf("ERROR! No loader binary AND IMAGE parameter not set!\n"); return CMD_RET_FAILURE; } } else if (env_get_hex("reformatOnUpgrade", 0)) { printf("INFO: Reformatting partitions because reformatOnUpgrade == 1!\n"); err = board_init_flash_parts(1); env_set("reformatOnUpgrade", NULL); } for( i=0; i 1) { if ( !strcmp(argv[1], "-s")) { download = false; argc--; argv++; } else if ( !strcmp(argv[1], "-i")) { commit = false; argc--; argv++; } } if( argc == 2 ) { img_index = get_img_index_for_upgrade(0); } else if( argc > 2 ) { if( strlen(argv[2]) == 1 ) img_index = *argv[2] - '0'; else { conf_name = argv[2]; img_index = get_img_index_for_upgrade(0); } if( argc > 3 ) { conf_name = argv[3]; } } if( img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX ) { printf("ERROR: Invalid Image Index specified!\n"); return CMD_RET_USAGE; } if (download) sprintf(cmd, "tftp %lx %s", load_addr, argv[1]); if( !download || run_command(cmd, 0) == 0 ) { bundle_addr = env_get_hex("fileaddr", 0); if (bundle_addr) /* Call main img flashing function */ err = flash_upgrade_img_bundle(bundle_addr, img_index, conf_name); } if(err) { printf("ERROR: Image upgrade failed!!\n"); } else { printf("INFO: Image upgrade successfull!!\n"); if( commit ) { printf("INFO: Committing Image!!\n"); commit_image( img_index ); /* Handle bootstrap condition */ if( active_img_idx == ACTIVE_IMGIDX_BOOTSTRAP ) { /* Now that we have a valid image flashed, change active image index */ set_active_img_idx(img_index); } } else { printf("INFO: Not Committing Image!!\n"); } } return err; } static int do_restoredefault(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { int ret = -1; if( strcmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) { #ifdef BCA_SDK_NAND_CMD ret = nand_restoredefault(); #endif } if( ret && ( strcmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) { #ifdef BCA_SDK_EMMC_CMD ret = emmc_restoredefault(); #endif } if( ret && ( strcmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) { #ifdef BCA_SDK_SPINOR_CMD ret = spinor_restoredefault(); #endif } return ret; } static int do_metadata(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { int ret = -1; int committed = 0; int valid[2] = {0,0}; int seq[2] = {-1,-1}; int status = get_metadata_val(&committed, valid, seq); if (argc == 1) { if (0 == status) { printf("committed-img: %d valid-imgs: ", committed); if( valid[0] ) printf("%d", valid[0]); if( valid[1] ) printf("%d", valid[1]); printf(", seq# img 1: %d, img2: %d\n\n", seq[0], seq[1]); ret = 0; } else { printf("metadata parse error\n"); } } else if (argc == 3) { committed = atoi(argv[1]); valid[0] = atoi(strtok(argv[2],",")); valid[1] = atoi(strtok(NULL,",")); if (0 != set_metadata_val(&committed, valid, seq)) { printf("metadata parse error\n"); } else { ret = 0; } } return ret; } #if defined(CONFIG_BCM_BOOTSTATE) static int do_activate(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { bcmbca_set_boot_reason(BCM_BOOT_REASON_ACTIVATE); run_command("reset", 0); return 0; } #endif static int do_force(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { int ret = 0; if( argc > 1 ) { if( atoi(argv[1]) == 1 ) { if( (argc == 4) && ( ( strcasecmp(argv[2], FLASH_DEV_STR_NAND) == 0 ) || ( strcasecmp(argv[2], FLASH_DEV_STR_SPINOR) == 0 ) || ( strcasecmp(argv[2], FLASH_DEV_STR_EMMC) == 0 ) ) && ( ( strcasecmp(argv[3], FLASH_DEV_STR_NAND) == 0 ) || ( strcasecmp(argv[3], FLASH_DEV_STR_SPINOR) == 0 ) || ( strcasecmp(argv[3], FLASH_DEV_STR_EMMC) == 0 ) ) ) { forced_updates = 1; strcpy(forced_boot_media,argv[2]); strcpy(forced_image_media,argv[3]); } else if(argc == 2) { forced_updates = 1; } else { ret = CMD_RET_USAGE; printf("Cannot determine boot/image flash type, please specify flash types!\n"); } } else { forced_updates = 0; forced_boot_media[0] = '\0'; forced_image_media[0] = '\0'; } } printf("Forced Image Updates: %s, Forced flash types: Boot=%s, Image=%s\n", (forced_updates?"Enabled":"Disabled"), (forced_updates?forced_boot_media:"NOT SPECIFIED"), (forced_updates?forced_image_media:"NOT SPECIFIED")); return ret; } #ifdef CONFIG_BCMBCA_HTTPD static int do_httpd_start(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { if(!httpd_check_net_env()) register_cli_job_cb(0, http_poll); return 0; } #endif #ifdef CONFIG_BCMBCA_PMC static int do_bpcm_cmd(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { int err = CMD_RET_USAGE; int read = 0; u32 addr, off, data; if ((argc != 5) && (argc != 4)) return err; if (!strcmp(argv[1], "r")) read = 1; else if (!strcmp(argv[1], "w")) read = 0; else return err; addr = atoi(argv[2]); off = atoi(argv[3]); if (!read) { data = atoi(argv[4]); err = WriteBPCMRegister(addr, off>>2, data); printf("bpcm write data 0x%x to addr 0x%x offset 0x%x ret %d\n", data, addr, off, err); } else { err = ReadBPCMRegister(addr, off>>2, &data); printf("bpcm read data 0x%x from addr 0x%x offset 0x%x ret %d\n", data, addr, off, err); } return err; } static int do_cpufreq(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { int err = CMD_RET_USAGE, freqMHz; if (argc != 2) return err; err = CMD_RET_SUCCESS; freqMHz = atoi(argv[1]); freqMHz = set_cpu_freq(freqMHz); if (freqMHz > 0) printf("cpu freq set to %dMHz\n", freqMHz); else err = CMD_RET_FAILURE; return err; } #endif static int do_dev_spec_key(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { int err = CMD_RET_USAGE; int rc, len; u32 dev_spec_key[8]; int i; bool force = false; if ( (argc == 2) && (strcmp(argv[1], "get") == 0)) { rc = bcm_sec_get_dev_spec_key((char*)dev_spec_key, sizeof(u32)*8); } else if ( (argc >= 10) && (strcmp(argv[1], "set") == 0)) { /* Parse and remove the optional FORCE argument */ if( argc > 10 ) { if ( !strcasecmp(argv[2], "-f")) { force = true; argc--; argv++; } } for(i=0; i<8; i++) { dev_spec_key[i] = simple_strtoul(argv[i+2],NULL, 0); } if ( !force ) { printf("WARNING: You are attempting to set a new device specific key.\n"); len = cli_readline("This operation cannot be undone, enter 'Y' to continue: "); if( !len || console_buffer[0] != 'Y' ) { printf("Device specific key setting aborted.\n"); return CMD_RET_SUCCESS; } } rc = bcm_sec_set_dev_spec_key((char*)dev_spec_key, sizeof(u32)*8); } else { return err; } if (rc == 0) { printf("Current Device Specific Key:\n"); for( i=0; i<8; i++ ) { printf(" [%d]0x%08x\n", i, dev_spec_key[i]); } printf("\n\n"); err = CMD_RET_SUCCESS; } else { err = CMD_RET_FAILURE; } return err; } static int do_sec_ser_num(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { int err = CMD_RET_USAGE; int rc, len; u32 sec_ser_num[8] = {0}; char * sec_ser_num_ptr = (char*)&sec_ser_num[0]; char * sec_ser_num_char_array; char byte_buf[3] = {0}; int i,j; bool force = false; bool is_set = false; bool is_odd = false; if ( (argc == 2) && (strcmp(argv[1], "get") == 0)) { rc = bcm_sec_get_sec_ser_num((char*)sec_ser_num, sizeof(u32)*8); } else if ( (argc >= 3) && (strcmp(argv[1], "set") == 0)) { is_set = true; /* Parse and remove the optional FORCE argument */ if( argc == 4 ) { if ( !strcasecmp(argv[2], "-f")) { force = true; argc--; argv++; } } /* Check if byte array exceeds 32 bytes OR contains an ODD number of characters */ sec_ser_num_char_array = argv[2]; /* Skip leading 0x if present */ if( sec_ser_num_char_array[1] == 'x' || sec_ser_num_char_array[1] == 'X' ) sec_ser_num_char_array += 2; debug("sernum:%s len:%d\n", sec_ser_num_char_array, (int)strlen(sec_ser_num_char_array)); if( ((strlen(sec_ser_num_char_array)/2) > sizeof(sec_ser_num)) || (strlen(sec_ser_num_char_array) % 2)){ return CMD_RET_USAGE; } /* Check if there is an odd number of digits so that we can insert an extra zero */ if( strlen(sec_ser_num_char_array) % 2 ) is_odd = 1; for(i=0,j=sizeof(sec_ser_num)-strlen(sec_ser_num_char_array)/2; i= 3) && (strcmp(argv[1], "set") == 0)) { /* Parse and remove the optional FORCE argument */ if( argc > 3 ) { if ( !strcasecmp(argv[2], "-f")) { force = true; argc--; argv++; } } /* get level */ lvl = atoi(argv[2]); if ( !force ) { printf("WARNING: You are attempting to set a new anti-rollback level.\n"); len = cli_readline("This operation cannot be undone, enter 'Y' to continue: "); if( !len || console_buffer[0] != 'Y' ) { printf("Anti-rollback level setting aborted.\n"); return CMD_RET_SUCCESS; } } rc = bcm_sec_set_antirollback_lvl(lvl); } else { return err; } if (rc == 0) { printf("Current anti-rollback level set to: %d\n", lvl); err = CMD_RET_SUCCESS; } else { err = CMD_RET_FAILURE; } return err; } #ifdef CONFIG_BCMBCA_XRDP_ETH extern int bcmbca_xrdp_eth_phy_status(void); extern int bcmbca_xrdp_eth_mac_status(void); extern int bcmbca_xrdp_eth_active_port_get(void); extern int bcmbca_xrdp_eth_active_port_set(int port); extern int bcmbca_xrdp_eth_env_active_port_set(int port); static int do_eth_active_port(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) { int port; if (argc == 2) { port = (atoi(argv[1])); bcmbca_xrdp_eth_active_port_set(port); } port = bcmbca_xrdp_eth_active_port_get(); printf("XRDP Ethernet active port is set to %d\n" ,port); return 0; } static int on_active_port(const char *name, const char *value, enum env_op op, int flags) { int active_port = -1; if ((flags & H_INTERACTIVE) == 0) return 0; switch (op) { case env_op_create: case env_op_overwrite: active_port = simple_strtoul(value, NULL, 10); break; case env_op_delete: break; default: break; } return bcmbca_xrdp_eth_env_active_port_set(active_port); } U_BOOT_ENV_CALLBACK(active_port, on_active_port); #endif static char usage[] = #ifdef BCA_SDK_EMMC_CMD "\n## eMMC Specific Test Commands ##\n" "gpt_fixup\n" " - Create GPT partitions if partition sizes are specfied via special uboot\n" " - environment variables. The env variable name has to be in the format:\n" " - _vol_size=\n" #endif /* BCA_SDK_EMMC_CMD */ #ifdef BCA_SDK_SPINOR_CMD "\n## SPINOR Specific Test Commands ##\n" "flash_spinor_fit\n" " - Download and flash atf+uboot+vmlinux+dtb in brcm_full.itb to SPI NOR flash \n" "flash_spinor_rootfs\n" " - Download and flash rootfs in rootfs.squashfs to SPI NOR flash\n" "flash_spinor_upgrade [-s] [conf]\n" "flash_spinor_upgrade [ []]\n" " - Download and flash an image upgrade bundle to SPI NOR flash\n" "flash_spinor_binary \n" " - Erase whole flash,download and flash a binary image to SPI NOR flash start from 0\n" "boot_spinor [boardid]\n" " - Boot linux from image in SPI NOR flash.\n" #endif "\n## Generic Test Commands ##\n" "force \n" " - Without arguments, display if forced image updates are enabled\n" "force [force value] <[Boot Flash Type] [Image Flash Type]>\n" " - Enable(value=1)/Disable(value=0) forced image updates. All compatibility checks are ignored if enabled\n" " - If boot and image flash types are not detected, then they need to be specified as 2nd and 3rd args\n" " - Valid flash type values = [NAND|SPINAND|EMMC|NOR]\n" "restoredefault\n" " - Delete contents of /data so that it gets populated with default values\n" "flash_loader\n" " - Download and flash SPL+TPL in loader_test__.bin to boot flash\n" #if defined(BCA_SDK_NAND_CMD) || defined(BCA_SDK_EMMC_CMD) "flash_bootfs_raw [img]\n" "flash_rootfs_raw [img]\n" " - Download and flash raw bootfs/rootfs binary to flash \n" " - WARNING: These are raw writes with no content, security or compatibility checks. Metadata is NOT updated \n" "metadata \n" "metadata [committed] [valid],[valid]\n" " - without arguments, parse and display metadata\n" " - set metadata values (example \"metadata 1 1,2\" for both images valid and #1 committed)\n" " - NOTE: Must do a sw reset for metadata changes to take affect\n" #endif "flash_img_upgrade [-s] [conf]\n" "flash_img_upgrade [-i] [ []]\n" " - Download and flash an image upgrade bundle via TFTP\n" " - NOTE: Must do a sw reset to use the new image\n" "load_img [boardid]\n" "load_img [ []]\n" " - Load image from flash into DDR.\n" "boot_img [boardid]\n" "boot_img [ []]\n" " - Boot linux from image in flash.\n" "[Legend - Optional Parameters]\n" " -s: Skip downloading the image via tftp. Assume it is already at ${fileaddr}\n" " -i: Inactive. Do not change activation state of new image i.e do not commit image\n" " img: Image index for flashing/booting. Valid values: [1|2]. Default index is determined from metadata\n" " fstype: Filesystem type for flashing. Valid values: [squashfs|ubifs|ext4]. Default is squashfs\n" " boardid: Boardid to determine boot configuration. If omitted default configuration node is used\n" " conf: Configuration name used for flashing img bundle. If omitted default configuration node is used\n\n" #ifdef CONFIG_BCMBCA_HTTPD "httpd_start \n" " - without arguments, start httpd server\n" #endif #ifdef CONFIG_BCMBCA_PMC "bpcm_cmd []\n" " - read/write bpcm register\n" "cpufreq \n" " - set cpu frequency\n" #endif "antirollback get\n" " - get current anti-rollback level\n" "antirollback set [-f] \n" " - set new anti-rollback level. [-f] will force the commit and skip warnings\n" "sec_ser_num get \n" " - get current secure serial number level ( 64 digits )\n" "sec_ser_num set [-f] < Hex secure serial number ( Max 64 digits )>\n" " - set new secure serial number . [-f] will force the commit and skip warnings\n" "dev_spec_key get \n" " - get current device specific key ( 32 bytes )\n" "dev_spec_key set [-f] <32-bit hex word 0>...<32-bit hex word 7> \n" " - set new device specific key. [-f] will force the commit and skip warnings\n" ; static char sdk_usage[] = "\nforce\n" " - Without arguments, display if forced image updates are enabled\n" "force [force value] <[Boot Flash Type] [Image Flash Type]>\n" " - Enable(value=1)/Disable(value=0) forced image updates. All compatibility checks are ignored if enabled\n" " - If boot and image flash types are not detected, then they need to be specified as 2nd and 3rd args\n" " - Valid flash type values = [NAND|SPINAND|EMMC|NOR]\n" "restoredefault\n" " - Delete contents of /data so that it gets populated with default values\n" #if defined(BCA_SDK_NAND_CMD) || defined(BCA_SDK_EMMC_CMD) "metadata \n" "metadata [committed] [valid],[valid]\n" " - without arguments, parse and display metadata\n" " - set metadata values (example \"metadata 1 1,2\" for both images valid and #1 committed)\n" " - NOTE: Must do a sw reset for metadata changes to take affect\n" #endif "flash_img_upgrade [-s] [conf]\n" "flash_img_upgrade [-i] [ []]\n" " - Download and flash an image upgrade bundle via TFTP\n" " - NOTE: Must do a sw reset to use the new image\n" "load_img [boardid]\n" "load_img [ []]\n" " - Load image from flash into DDR.\n" "boot_img [boardid]\n" "boot_img [ []]\n" " - Boot linux from image in flash.\n" "[Legend - Optional Parameters]\n" " -s: Skip downloading the image via tftp. Assume it is already at ${fileaddr}\n" " -i: Inactive. Mark new image as inactive i.e do not commit image\n" " img: Image index for flashing/booting. Valid values: [1|2]. Default index is determined from metadata\n" " boardid: Boardid to determine boot configuration. If omitted default configuration node is used\n" " conf: Configuration name used for flashing img bundle. If omitted default configuration node is used\n\n" #ifdef CONFIG_BCMBCA_HTTPD "httpd_start \n" " - without arguments, start httpd server\n" #endif #ifdef CONFIG_BCMBCA_XRDP_ETH "active_port [port]\n" " - Set the active network driver's port to send packets from\n" " - without arguments, display the current active port\n" "eth_status [type]\n" " - Print the XRDP Ethernet network driver status tables\n" " - Argument can be phy or mac. If no argument provided, both tables will be printed\n" #endif ; U_BOOT_CMD_WITH_SUBCMDS(bca_test, "Broadcom test commands", usage, U_BOOT_SUBCMD_MKENT(flash_loader, 1, 0, do_flash_loader), #ifdef BCA_SDK_EMMC_CMD U_BOOT_SUBCMD_MKENT(gpt_fixup, 1, 0, do_gpt_fixup), #endif /* BCA_SDK_EMMC_CMD */ #ifdef BCA_SDK_SPINOR_CMD U_BOOT_SUBCMD_MKENT(flash_spinor_fit, 1, 0, do_flash_spinor_bootfs_rootfs), U_BOOT_SUBCMD_MKENT(flash_spinor_rootfs, 1, 0, do_flash_spinor_bootfs_rootfs), U_BOOT_SUBCMD_MKENT(flash_spinor_upgrade, 5, 0, do_flash_upgrade_img), U_BOOT_SUBCMD_MKENT(flash_spinor_binary, 2, 0, do_flash_spinor_binary), U_BOOT_SUBCMD_MKENT(boot_spinor, 3, 0, do_boot), #endif #if defined(CONFIG_BCM_BOOTSTATE) U_BOOT_SUBCMD_MKENT(activate, 1, 0, do_activate), #endif /* CONFIG_BCM_BOOTSTATE */ U_BOOT_SUBCMD_MKENT(force, 4, 0, do_force), U_BOOT_SUBCMD_MKENT(restoredefault, 1, 0, do_restoredefault), #if defined(BCA_SDK_NAND_CMD) || defined(BCA_SDK_EMMC_CMD) U_BOOT_SUBCMD_MKENT(flash_bootfs_raw, 3, 0, do_flash_bins), U_BOOT_SUBCMD_MKENT(flash_rootfs_raw, 3, 0, do_flash_bins), U_BOOT_SUBCMD_MKENT(metadata, 5, 0, do_metadata), #endif U_BOOT_SUBCMD_MKENT(flash_img_upgrade, 5, 0, do_flash_upgrade_img), U_BOOT_SUBCMD_MKENT(boot_img, 3, 0, do_boot) #ifdef CONFIG_BCMBCA_HTTPD ,U_BOOT_SUBCMD_MKENT(httpd_start, 1, 0, do_httpd_start) #endif #ifdef CONFIG_BCMBCA_PMC ,U_BOOT_SUBCMD_MKENT(bpcm_cmd, 5, 0, do_bpcm_cmd) ,U_BOOT_SUBCMD_MKENT(cpufreq, 2, 0, do_cpufreq) #endif ,U_BOOT_SUBCMD_MKENT(antirollback, 4, 0, do_antirollback) ,U_BOOT_SUBCMD_MKENT(sec_ser_num, 12, 0, do_sec_ser_num) ,U_BOOT_SUBCMD_MKENT(dev_spec_key, 12, 0, do_dev_spec_key) ); U_BOOT_CMD_WITH_SUBCMDS(sdk, "Broadcom SDK support commands", sdk_usage, U_BOOT_SUBCMD_MKENT(force, 4, 0, do_force), U_BOOT_SUBCMD_MKENT(restoredefault, 1, 0, do_restoredefault), #if defined(BCA_SDK_NAND_CMD) || defined(BCA_SDK_EMMC_CMD) U_BOOT_SUBCMD_MKENT(metadata, 5, 0, do_metadata), #endif U_BOOT_SUBCMD_MKENT(flash_img_upgrade, 5, 0, do_flash_upgrade_img), U_BOOT_SUBCMD_MKENT(boot_img, 3, 0, do_boot), U_BOOT_SUBCMD_MKENT(load_img, 3, 0, do_load) #ifdef CONFIG_BCMBCA_HTTPD ,U_BOOT_SUBCMD_MKENT(httpd_start, 1, 0, do_httpd_start) #endif #ifdef CONFIG_BCMBCA_XRDP_ETH ,U_BOOT_SUBCMD_MKENT(active_port, 2, 0, do_eth_active_port) #endif );