// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2000-2010 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2019 Broadcom Corporation * Joel Peshkin, Broadcom Corporation, joel.peshkin@broadcom.com */ #define DEBUG #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; __weak uint32_t env_boot_magic_search_size(void) { return 2*1024*1024; } __weak int env_override_import(void *ep) { return 0; } #ifdef CONFIG_SPI_FLASH static int env_dev_read_spinor( int addr, size_t * len, char * buffer ) { int ret= -1; struct mtd_info *mtd; size_t retlen; mtd = get_mtd_device_nm(LOADER_PART); if (IS_ERR_OR_NULL(mtd)){ debug("%s:MTD device %s not found, ret %ld\n",__func__, LOADER_PART, PTR_ERR(mtd)); return ret; } ret = mtd_read(mtd,addr,*len,&retlen,buffer); put_mtd_device(mtd); return ret; } static int env_dev_write_spinor( int addr, size_t * len, char * buffer ) { int ret= -1; struct mtd_info *mtd; struct erase_info ei; size_t retlen; u_char * tempbuff = NULL; size_t erase_bytes; u32 start_offset; u32 start_sector; u32 end_sector; mtd = get_mtd_device_nm(LOADER_PART); if (IS_ERR_OR_NULL(mtd)){ debug("%s:MTD device %s not found, ret %ld\n",__func__, LOADER_PART, PTR_ERR(mtd)); return ret; } start_sector = addr/mtd->erasesize; end_sector = ( addr + *len + mtd->erasesize - 1)/mtd->erasesize; start_offset = start_sector * mtd->erasesize; erase_bytes = (end_sector - start_sector)*mtd->erasesize; tempbuff = malloc(erase_bytes); if (!tempbuff){ debug("%s malloc %d failed\n",__func__, erase_bytes); goto done; } ret = mtd_read(mtd,start_offset,erase_bytes,&retlen,tempbuff); if (ret){ debug("%s:read MTD device %s fail, ret %d\n",__func__, LOADER_PART, ret); goto done; } memcpy(tempbuff + addr%mtd->erasesize, buffer, *len); memset(&ei, 0, sizeof(ei)); ei.mtd = mtd; ei.addr = start_offset; ei.len = erase_bytes; ret = mtd_erase(mtd, &ei); if (ret){ debug("%s:erase MTD device %s fail, ret %d\n",__func__, LOADER_PART, ret); goto done; } ret = mtd_write(mtd, start_offset, erase_bytes, &retlen, tempbuff); if (ret){ debug("%s:write MTD device %s fail, ret %d\n",__func__, LOADER_PART, ret); } done: free(tempbuff); put_mtd_device(mtd); return ret; } #endif #ifdef CONFIG_NAND static int env_dev_read_nand( int addr, size_t * len, char * buffer ) { int ret= -1; struct mtd_info *mtd; mtd = get_nand_dev_by_index(0); if (!mtd) return ret; ret = nand_read(mtd, addr, len, (void *)buffer); return ret; } static int env_dev_write_nand( int addr, size_t * len, char * buffer ) { int blocknum, off; size_t blocksize; int ret = -1; struct mtd_info *mtd; char * block = NULL; mtd = get_nand_dev_by_index(0); if (!mtd) return ret; block = malloc(mtd->erasesize); if (!block) return ret; blocksize = mtd->erasesize; blocknum = addr / mtd->erasesize; off = addr % mtd->erasesize; ret = nand_read(mtd, (off_t) (blocknum * mtd->erasesize), &blocksize, (void *)block); if (ret < 0) { debug("read error %d on block %d\n", ret, blocknum); } memcpy(block + off, buffer, *len); nand_erase(mtd, blocknum * blocksize, blocksize); ret = nand_write(mtd, blocknum * blocksize, &blocksize, (void *)block); if (ret < 0) { debug("write error %d on block %d\n", ret, blocknum); } free(block); return ret; } #endif #ifdef CONFIG_MMC #define ENV_DEV_MMC_READ 0 #define ENV_DEV_MMC_WRITE 1 #define ENV_DEV_MMC_DEV_NUM 0 #define ENV_DEV_MMC_BOOT_HWPART 1 #define ENV_DEV_MMC_USERDATA_PART 0 static int env_dev_read_write_mmc( int addr, size_t * len, char * buffer, int mode) { uint64_t blocksize, start_blocknum, start_offset, num_blocks; int ret = -1; char * block_data_ptr = NULL; struct mmc *mmc = NULL; struct blk_desc * block_dev = NULL; /* Initialize MMC */ mmc = find_mmc_device(ENV_DEV_MMC_DEV_NUM); if (!mmc) return ret; mmc_init(mmc); /* Point to BOOT partition */ blk_select_hwpart_devnum(IF_TYPE_MMC, ENV_DEV_MMC_DEV_NUM, ENV_DEV_MMC_BOOT_HWPART); /* Get block device */ block_dev = mmc_get_blk_desc(mmc); if (!block_dev) goto mmc_err; /* Calculate block numbers and offsets */ blocksize = mmc->read_bl_len; start_blocknum = addr/blocksize; start_offset = addr % blocksize; num_blocks = (start_offset+*len)/blocksize + ((start_offset+*len)%blocksize?1:0); block_data_ptr = memalign(ARCH_DMA_MINALIGN, num_blocks*blocksize); if( !block_data_ptr ) { printf("%s: ERROR! Cannot allocate memory for blockdata\n", __FUNCTION__); goto mmc_err; } ret = blk_dread(block_dev, start_blocknum, num_blocks, (void *)block_data_ptr); if (ret < 0) { printf("%s: read error %d on block %llu, num_blocks %llu\n", __FUNCTION__, ret, start_blocknum, num_blocks); goto mmc_err; } if( mode == ENV_DEV_MMC_WRITE ) { memcpy(block_data_ptr + start_offset, buffer, *len); ret = blk_dwrite(block_dev, start_blocknum, num_blocks, (void *)block_data_ptr); if (ret < 0) { printf("%s: write error %d on block %llu, num_blocks %llu\n", __FUNCTION__, ret, start_blocknum, num_blocks); goto mmc_err; } } else { memcpy(buffer, block_data_ptr + start_offset, *len); } ret = 0; mmc_err: if( block_data_ptr ) free(block_data_ptr); /* Point to USERDATA partition */ blk_select_hwpart_devnum(IF_TYPE_MMC, ENV_DEV_MMC_DEV_NUM, ENV_DEV_MMC_USERDATA_PART); return ret; } #endif static int get_env_flashtype( char ** flashtype ) { *flashtype = get_loader_media(); return 0; } static int env_dev_read( int addr, size_t * len, char * buffer ) { char * pflashtype_str = NULL; if( get_env_flashtype(&pflashtype_str) == 0 ) { if( pflashtype_str != NULL ) { #ifdef CONFIG_NAND if( strcasecmp(pflashtype_str,FLASH_DEV_STR_NAND) == 0 ) { return (env_dev_read_nand(addr, len, buffer)); } #endif #ifdef CONFIG_MMC if( strcasecmp(pflashtype_str,FLASH_DEV_STR_EMMC) == 0 ) { return (env_dev_read_write_mmc(addr, len, buffer, ENV_DEV_MMC_READ)); } #endif #ifdef CONFIG_SPI_FLASH if( strcasecmp(pflashtype_str,FLASH_DEV_STR_SPINOR) == 0 ) { return (env_dev_read_spinor(addr, len, buffer)); } #endif printf("%s: Error: Unsupported env storage media %s!\n", __FUNCTION__, pflashtype_str); } else { printf("%s: Error: Environment storage media invalid!\n", __FUNCTION__); } } else { printf("%s: Error: Cannot find environment storage media!\n", __FUNCTION__); } return -1; } static int env_dev_write( int addr, size_t * len, char * buffer ) { char * pflashtype_str = NULL; if( get_env_flashtype(&pflashtype_str) == 0 ) { if( pflashtype_str != NULL ) { #ifdef CONFIG_NAND if( strcasecmp(pflashtype_str,FLASH_DEV_STR_NAND) == 0 ) { return (env_dev_write_nand(addr, len, buffer)); } #endif #ifdef CONFIG_MMC if( strcasecmp(pflashtype_str,FLASH_DEV_STR_EMMC) == 0 ) { return (env_dev_read_write_mmc(addr, len, buffer, ENV_DEV_MMC_WRITE)); } #endif #ifdef CONFIG_SPI_FLASH if( strcasecmp(pflashtype_str,FLASH_DEV_STR_SPINOR) == 0 ) { return (env_dev_write_spinor(addr, len, buffer)); } #endif } } printf("%s: Error: Cannot find environment storage media!\n", __FUNCTION__); return -1; } static int env_boot_magic_load(void) { uint32_t crc, new; int ret; loff_t off, copies[10]; int copies_found = 0; int found_size; size_t rdlen; env_t *ep; uint32_t magichdr[3]; char *ebuff = NULL; printf("ENV_BOOT_MAGIC_LOAD\n"); for (off = 0; off < (loff_t)env_boot_magic_search_size(); off += 4096) { rdlen = 12; env_dev_read( off, &rdlen, (void *)magichdr); if (magichdr[0] != BOOT_MAGIC_MAGIC) { continue; } printf("found magic at %lx\n", (long)off); rdlen = magichdr[1] + 12; if (ebuff == NULL) { ebuff = malloc(max((int)rdlen, CONFIG_ENV_SIZE + 12)); } env_dev_read( off, &rdlen, (void *)ebuff); ep = (env_t *) & ebuff[8]; memcpy(&crc, &ep->crc, sizeof(crc)); new = crc32(0, ep->data, rdlen - 16); if (new != crc) { debug("CRC mismatch len = %d\n", (int)rdlen - 16); debug("computed %x \n", new); debug("expected %x \n", crc); } else { debug("good crc\n"); gd->env_valid = ENV_VALID; /* FIXME -- may need to grow instead of shrink */ debug("resize from %ld to %d\n", rdlen - 12, CONFIG_ENV_SIZE); new = crc32(0, ep->data, CONFIG_ENV_SIZE - 4); memcpy(&ep->crc, &new, sizeof(new)); copies[copies_found++] = off; found_size = rdlen - 12; if (!env_override_import((void *)ep)) { ret = env_import((void *)ep, 1); } } free(ebuff); ebuff = NULL; if (NULL != env_get("env_boot_magic")) { break; } } if (copies_found > 0) { if (NULL == env_get("env_boot_magic")) { char found[256]; int i; sprintf(found, "%d@", found_size); for (i = 0; i < copies_found; i++) { sprintf(found + strlen(found), "0x%llx,", copies[i]); } found[strlen(found) - 1] = '\0'; env_set("env_boot_magic", found); } return (ret); } else { set_default_env("import not done", 0); return (-1); } } static int env_boot_magic_save(void) { env_t *ep; char *envbuf = NULL; uint32_t *envintp; char *c; int ret = -2; int elen; size_t wr_len; char *config; uint32_t new; int i; char *found = NULL; config = env_get("env_boot_magic"); if (NULL == config) return (-1); elen = simple_strtoul(config, NULL, 0); found = malloc(strlen(config) + 1); if (!found) goto err; strcpy(found, config); envbuf = malloc(max(elen + 12, CONFIG_ENV_SIZE + 12)); ep = (env_t *) (envbuf + 8); envintp = (uint32_t *) envbuf; ret = env_export(ep); for (i = CONFIG_ENV_SIZE; i < elen; i++) { envbuf[12 + i] = 0xff; } new = crc32(0, ep->data, elen - 4); memcpy(&ep->crc, &new, sizeof(new)); envintp[0] = BOOT_MAGIC_MAGIC; envintp[1] = elen; strtok(found, "@"); while ((c = strtok(NULL, ","))) { i = simple_strtoul(c, NULL, 0); printf("save to %x\n", i); wr_len = elen+12; ret = env_dev_write( i, &wr_len, envbuf ); } err: free(envbuf); free(found); return ret; } U_BOOT_ENV_LOCATION(boot_magic) = { .location = ENVL_BOOT_MAGIC, .load = env_boot_magic_load, .save = env_save_ptr(env_boot_magic_save), ENV_NAME("BOOT_MAGIC") };