diff options
author | Han Xu <han.xu@nxp.com> | 2020-05-05 22:04:03 +0800 |
---|---|---|
committer | Stefano Babic <sbabic@denx.de> | 2020-05-10 20:55:20 +0200 |
commit | 214b7d534d420e9377d7f9e4c364123e33b0233d (patch) | |
tree | 1f6909a8bf15fc152a3138df59e19d3983493832 /arch/arm | |
parent | 49f84547057f4c4c268c0567a11052a8975450e2 (diff) | |
download | u-boot-214b7d534d420e9377d7f9e4c364123e33b0233d.tar.gz |
cmd: nandbcb: Reconstruct the nandbcb tool for all platforms
The original nandbcb tool was designed for imx6 only, when trying to
leverage it to replace the kobs-ng tool, we found the design is not
friendly for supporting all platforms. To support all iMX6/7/8 platforms
and for easy further maintain, I reconstruct the structure of the tool.
The main changes including:
1. Use platform_data to determine the logic branches rather than simply
use SOC name.
2. More data structures as parameter for functions.
3. Global variables to define the FCB/DBBT/FW locations.
4. Implement the kobs-ng default 4 FCB/4 DBBT/2 FW layout.
5. Support Hamming coding/ 40bit BCH/ 62bit BCH coding FCB.
6. Dump and compare all written FCB/DBBT to verify data integrity.
The tool has been verified on iMX6Q/DL, 6SX, 7D, 6ULL, iMX8QX, iMX8MM.
Signed-off-by: Han Xu <han.xu@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/include/asm/mach-imx/imx-nandbcb.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-imx/cmd_nandbcb.c | 1303 |
2 files changed, 955 insertions, 352 deletions
diff --git a/arch/arm/include/asm/mach-imx/imx-nandbcb.h b/arch/arm/include/asm/mach-imx/imx-nandbcb.h index 907e7ed8f9..74c9031d4e 100644 --- a/arch/arm/include/asm/mach-imx/imx-nandbcb.h +++ b/arch/arm/include/asm/mach-imx/imx-nandbcb.h @@ -9,9 +9,11 @@ #define FCB_FINGERPRINT 0x20424346 /* 'FCB' */ #define FCB_VERSION_1 0x01000000 +#define FCB_FINGERPRINT_OFF 0x4 /* FCB fingerprint offset*/ -#define DBBT_FINGERPRINT2 0x54424244 /* 'DBBT' */ +#define DBBT_FINGERPRINT 0x54424244 /* 'DBBT' */ #define DBBT_VERSION_1 0x01000000 +#define DBBT_FINGERPRINT_OFF 0x4 /* DBBT fingerprint offset*/ struct dbbt_block { u32 checksum; /* reserved on i.MX6 */ diff --git a/arch/arm/mach-imx/cmd_nandbcb.c b/arch/arm/mach-imx/cmd_nandbcb.c index a8531def35..ab12b1f1cf 100644 --- a/arch/arm/mach-imx/cmd_nandbcb.c +++ b/arch/arm/mach-imx/cmd_nandbcb.c @@ -1,11 +1,13 @@ /* - * i.MX6 nand boot control block(bcb). + * i.MX nand boot control block(bcb). * * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng * * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com> * Copyright (C) 2016 Sergey Kubushyn <ksi@koi8.net> * + * Reconstucted by Han Xu <han.xu@nxp.com> + * * SPDX-License-Identifier: GPL-2.0+ */ @@ -25,24 +27,295 @@ #include <mxs_nand.h> #include <linux/mtd/mtd.h> #include <nand.h> -#include <div64.h> #include "../../../cmd/legacy-mtd-utils.h" -#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET) +/* FCB related flags */ +/* FCB layout with leading 12B reserved */ +#define FCB_LAYOUT_RESV_12B BIT(0) +/* FCB layout with leading 32B meta data */ +#define FCB_LAYOUT_META_32B BIT(1) +/* FCB encrypted by Hamming code */ +#define FCB_ENCODE_HAMMING BIT(2) +/* FCB encrypted by 40bit BCH */ +#define FCB_ENCODE_BCH_40b BIT(3) +/* FCB encrypted by 62bit BCH */ +#define FCB_ENCODE_BCH_62b BIT(4) +/* FCB encrypted by BCH */ +#define FCB_ENCODE_BCH (FCB_ENCODE_BCH_40b | FCB_ENCODE_BCH_62b) +/* FCB data was randomized */ +#define FCB_RANDON_ENABLED BIT(5) + +/* Firmware related flags */ +/* No 1K padding */ +#define FIRMWARE_NEED_PADDING BIT(8) +/* Extra firmware*/ +#define FIRMWARE_EXTRA_ONE BIT(9) +/* Secondary firmware on fixed address */ +#define FIRMWARE_SECONDARY_FIXED_ADDR BIT(10) + +/* Boot search related flags */ +#define BT_SEARCH_CNT_FROM_FUSE BIT(16) + +struct platform_config { + int misc_flags; +}; + +static struct platform_config plat_config; + +/* imx6q/dl/solo */ +static struct platform_config imx6qdl_plat_config = { + .misc_flags = FCB_LAYOUT_RESV_12B | + FCB_ENCODE_HAMMING | + FIRMWARE_NEED_PADDING, +}; + +static struct platform_config imx6sx_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FIRMWARE_NEED_PADDING | + FCB_RANDON_ENABLED, +}; + +static struct platform_config imx7d_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FIRMWARE_NEED_PADDING | + FCB_RANDON_ENABLED, +}; + +/* imx6ul/ull/ulz */ +static struct platform_config imx6ul_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_40b | + FIRMWARE_NEED_PADDING, +}; + +static struct platform_config imx8mq_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FIRMWARE_NEED_PADDING | + FCB_RANDON_ENABLED | + FIRMWARE_EXTRA_ONE, +}; + +/* all other imx8mm */ +static struct platform_config imx8mm_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FIRMWARE_NEED_PADDING | + FCB_RANDON_ENABLED, +}; + +/* imx8mn */ +static struct platform_config imx8mn_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FCB_RANDON_ENABLED | + FIRMWARE_SECONDARY_FIXED_ADDR | + BT_SEARCH_CNT_FROM_FUSE, +}; + +/* imx8qx/qm */ +static struct platform_config imx8q_plat_config = { + .misc_flags = FCB_LAYOUT_META_32B | + FCB_ENCODE_BCH_62b | + FCB_RANDON_ENABLED | + FIRMWARE_SECONDARY_FIXED_ADDR | + BT_SEARCH_CNT_FROM_FUSE, +}; + +/* boot search related variables and definitions */ +static int g_boot_search_count = 4; +static int g_boot_search_stride; +static int g_pages_per_stride; + +/* mtd config structure */ +struct boot_config { + int dev; + struct mtd_info *mtd; + loff_t maxsize; + loff_t input_size; + loff_t offset; + loff_t boot_stream1_address; + loff_t boot_stream2_address; + size_t boot_stream1_size; + size_t boot_stream2_size; + size_t max_boot_stream_size; + int stride_size_in_byte; + int search_area_size_in_bytes; + int search_area_size_in_pages; + int secondary_boot_stream_off_in_MB; +}; + +/* boot_stream config structure */ +struct boot_stream_config { + char bs_label[32]; + loff_t bs_addr; + size_t bs_size; + void *bs_buf; + loff_t next_bs_addr; + bool need_padding; +}; + +/* FW index */ +#define FW1_ONLY 1 +#define FW2_ONLY 2 +#define FW_ALL FW1_ONLY | FW2_ONLY +#define FW_INX(x) (1 << (x)) + +/* NAND convert macros */ +#define CONV_TO_PAGES(x) ((u32)(x) / (u32)(mtd->writesize)) +#define CONV_TO_BLOCKS(x) ((u32)(x) / (u32)(mtd->erasesize)) + #define GETBIT(v, n) (((v) >> (n)) & 0x1) #define IMX8MQ_SPL_SZ 0x3e000 #define IMX8MQ_HDMI_FW_SZ 0x19c00 -#define BOOT_SEARCH_COUNT 2 -struct mtd_info *dump_mtd; -static loff_t dump_nandboot_size; -static struct fcb_block dump_fill_fcb; -static struct dbbt_block dump_fill_dbbt; -static struct fcb_block dump_nand_fcb[BOOT_SEARCH_COUNT]; -static struct dbbt_block dump_nand_dbbt[BOOT_SEARCH_COUNT]; -static u32 dump_fcb_off[BOOT_SEARCH_COUNT]; -static u32 dump_dbbt_off[BOOT_SEARCH_COUNT]; +static int nandbcb_get_info(int argc, char * const argv[], + struct boot_config *boot_cfg) +{ + int dev; + struct mtd_info *mtd; + + dev = nand_curr_device; + if (dev < 0) { + printf("failed to get nand_curr_device, run nand device\n"); + return CMD_RET_FAILURE; + } + + mtd = get_nand_dev_by_index(dev); + if (!mtd) { + printf("failed to get mtd info\n"); + return CMD_RET_FAILURE; + } + + boot_cfg->dev = dev; + boot_cfg->mtd = mtd; + + return CMD_RET_SUCCESS; +} + +static int nandbcb_get_size(int argc, char * const argv[], int num, + struct boot_config *boot_cfg) +{ + int dev; + loff_t offset, size, maxsize; + struct mtd_info *mtd; + + dev = boot_cfg->dev; + mtd = boot_cfg->mtd; + size = 0; + + if (mtd_arg_off_size(argc - num, argv + num, &dev, &offset, &size, + &maxsize, MTD_DEV_TYPE_NAND, mtd->size)) + return CMD_RET_FAILURE; + + boot_cfg->maxsize = maxsize; + boot_cfg->offset = offset; + + debug("max: %llx, offset: %llx\n", maxsize, offset); + + if (size && size != maxsize) + boot_cfg->input_size = size; + + return CMD_RET_SUCCESS; +} + +static int nandbcb_set_boot_config(int argc, char * const argv[], + struct boot_config *boot_cfg) +{ + struct mtd_info *mtd; + loff_t maxsize; + loff_t boot_stream1_address, boot_stream2_address, max_boot_stream_size; + + if (!boot_cfg->mtd) { + printf("Didn't get the mtd info, quit\n"); + return CMD_RET_FAILURE; + } + mtd = boot_cfg->mtd; + + /* + * By default + * set the search count as 4 + * set each FCB/DBBT/Firmware offset at the beginning of blocks + * customers may change the value as needed + */ + + /* if need more compact layout, change these values */ + /* g_boot_search_count was set as 4 at the definition*/ + /* g_pages_per_stride was set as block size */ + + g_pages_per_stride = mtd->erasesize / mtd->writesize; + + g_boot_search_stride = mtd->writesize * g_pages_per_stride; + + boot_cfg->stride_size_in_byte = g_boot_search_stride * mtd->writesize; + boot_cfg->search_area_size_in_bytes = + g_boot_search_count * g_boot_search_stride; + boot_cfg->search_area_size_in_pages = + boot_cfg->search_area_size_in_bytes / mtd->writesize; + + /* after FCB/DBBT, split the rest of area for two Firmwares */ + if (!boot_cfg->maxsize) { + printf("Didn't get the maxsize, quit\n"); + return CMD_RET_FAILURE; + } + maxsize = boot_cfg->maxsize; + /* align to page boundary */ + maxsize = ((u32)(maxsize + mtd->writesize - 1)) / (u32)mtd->writesize + * mtd->writesize; + + boot_stream1_address = 2 * boot_cfg->search_area_size_in_bytes; + boot_stream2_address = ((maxsize - boot_stream1_address) / 2 + + boot_stream1_address); + + if (boot_cfg->secondary_boot_stream_off_in_MB) + boot_stream2_address = boot_cfg->secondary_boot_stream_off_in_MB * 1024 * 1024; + + max_boot_stream_size = boot_stream2_address - boot_stream1_address; + + /* sanity check */ + if (max_boot_stream_size <= 0) { + debug("st1_addr: %llx, st2_addr: %llx, max: %llx\n", + boot_stream1_address, boot_stream2_address, + max_boot_stream_size); + printf("something wrong with firmware address settings\n"); + return CMD_RET_FAILURE; + } + boot_cfg->boot_stream1_address = boot_stream1_address; + boot_cfg->boot_stream2_address = boot_stream2_address; + boot_cfg->max_boot_stream_size = max_boot_stream_size; + + /* set the boot_stream size as the input size now */ + if (boot_cfg->input_size) { + boot_cfg->boot_stream1_size = boot_cfg->input_size; + boot_cfg->boot_stream2_size = boot_cfg->input_size; + } + + return CMD_RET_SUCCESS; +} + +static int nandbcb_check_space(struct boot_config *boot_cfg) +{ + size_t maxsize = boot_cfg->maxsize; + size_t max_boot_stream_size = boot_cfg->max_boot_stream_size; + loff_t boot_stream2_address = boot_cfg->boot_stream2_address; + + if (boot_cfg->boot_stream1_size && + boot_cfg->boot_stream1_size > max_boot_stream_size) { + printf("boot stream1 doesn't fit, check partition size or settings\n"); + return CMD_RET_FAILURE; + } + + if (boot_cfg->boot_stream2_size && + boot_cfg->boot_stream2_size > maxsize - boot_stream2_address) { + printf("boot stream2 doesn't fit, check partition size or settings\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} #if defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) static uint8_t reverse_bit(uint8_t b) @@ -145,9 +418,9 @@ static u32 calc_chksum(void *buf, size_t size) return ~chksum; } -static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd, - u32 fw1_start, u32 fw2_start, u32 fw_pages) +static void fill_fcb(struct fcb_block *fcb, struct boot_config *boot_cfg) { + struct mtd_info *mtd = boot_cfg->mtd; struct nand_chip *chip = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(chip); struct mxs_nand_layout l; @@ -157,6 +430,11 @@ static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd, fcb->fingerprint = FCB_FINGERPRINT; fcb->version = FCB_VERSION_1; + fcb->datasetup = 80; + fcb->datahold = 60; + fcb->addr_setup = 25; + fcb->dsample_time = 6; + fcb->pagesize = mtd->writesize; fcb->oob_pagesize = mtd->writesize + mtd->oobsize; fcb->sectors = mtd->erasesize / mtd->writesize; @@ -169,41 +447,25 @@ static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd, fcb->ecc_type = l.eccn; fcb->bchtype = l.gf_len; - /* Also hardcoded in kobs-ng */ - if (is_mx6() || is_imx8m()) { - fcb->datasetup = 80; - fcb->datahold = 60; - fcb->addr_setup = 25; - fcb->dsample_time = 6; - } else if (is_mx7()) { - fcb->datasetup = 10; - fcb->datahold = 7; - fcb->addr_setup = 15; - fcb->dsample_time = 6; - } - - /* DBBT search area starts at second page on first block */ - fcb->dbbt_start = 1; + /* DBBT search area starts from the next block after all FCB */ + fcb->dbbt_start = boot_cfg->search_area_size_in_pages; fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset; fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset; fcb->phy_offset = mtd->writesize; - fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1; - fcb->disbbm = 0; - fcb->disbbm_search = 0; - fcb->fw1_start = fw1_start; /* Firmware image starts on this sector */ - fcb->fw2_start = fw2_start; /* Secondary FW Image starting Sector */ - fcb->fw1_pages = fw_pages; /* Number of sectors in firmware image */ - fcb->fw2_pages = fw_pages; /* Number of sector in secondary FW image */ + fcb->fw1_start = CONV_TO_PAGES(boot_cfg->boot_stream1_address); + fcb->fw2_start = CONV_TO_PAGES(boot_cfg->boot_stream2_address); + fcb->fw1_pages = CONV_TO_PAGES(boot_cfg->boot_stream1_size); + fcb->fw2_pages = CONV_TO_PAGES(boot_cfg->boot_stream2_size); fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4); } -static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks) +static int fill_dbbt_data(struct mtd_info *mtd, void *buf, int num_blocks) { int n, n_bad_blocks = 0; u32 *bb = buf + 0x8; @@ -223,21 +485,92 @@ static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks) return n_bad_blocks; } -static int write_fcb_dbbt_and_readback(struct mtd_info *mtd, - struct fcb_block *fcb, - struct dbbt_block *dbbt, - void *dbbt_data_page, loff_t off) +/* + * return 1 - bad block + * return 0 - read successfully + * return < 0 - read failed + */ +static int read_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb, + loff_t off) { - void *fcb_raw_page = 0; + struct mtd_info *mtd; + void *fcb_raw_page; + size_t size; + int ret = 0; + + mtd = boot_cfg->mtd; + fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + + if (mtd_block_isbad(mtd, off)) { + printf("Block %d is bad, skipped\n", (int)CONV_TO_BLOCKS(off)); + return 1; + } + + /* + * User BCH hardware to decode ECC for FCB + */ + if (plat_config.misc_flags & FCB_ENCODE_BCH) { + size = sizeof(struct fcb_block); + + /* switch nand BCH to FCB compatible settings */ + if (plat_config.misc_flags & FCB_ENCODE_BCH_62b) + mxs_nand_mode_fcb_62bit(mtd); + else if (plat_config.misc_flags & FCB_ENCODE_BCH_40b) + mxs_nand_mode_fcb_40bit(mtd); + + ret = nand_read(mtd, off, &size, (u_char *)fcb); + + /* switch BCH back */ + mxs_nand_mode_normal(mtd); + printf("NAND FCB read from 0x%llx offset 0x%zx read: %s\n", + off, size, ret ? "ERROR" : "OK"); + + } else if (plat_config.misc_flags & FCB_ENCODE_HAMMING) { + /* raw read*/ + mtd_oob_ops_t ops = { + .datbuf = (u8 *)fcb_raw_page, + .oobbuf = ((u8 *)fcb_raw_page) + mtd->writesize, + .len = mtd->writesize, + .ooblen = mtd->oobsize, + .mode = MTD_OPS_RAW + }; + + ret = mtd_read_oob(mtd, off, &ops); + printf("NAND FCB read from 0x%llx offset 0x%zx read: %s\n", + off, ops.len, ret ? "ERROR" : "OK"); + } + + if (ret) + goto fcb_raw_page_err; + + if ((plat_config.misc_flags & FCB_ENCODE_HAMMING) && + (plat_config.misc_flags & FCB_LAYOUT_RESV_12B)) + memcpy(fcb, fcb_raw_page + 12, sizeof(struct fcb_block)); + +/* TODO: check if it can pass Hamming check */ + +fcb_raw_page_err: + kfree(fcb_raw_page); + + return ret; +} + +static int write_fcb(struct boot_config *boot_cfg, struct fcb_block *fcb) +{ + struct mtd_info *mtd; + void *fcb_raw_page = NULL; int i, ret; - size_t dummy; + loff_t off; + size_t size; + + mtd = boot_cfg->mtd; /* * We prepare raw page only for i.MX6, for i.MX7 we * leverage BCH hw module instead */ - if (is_mx6()) { - /* write fcb/dbbt */ + if ((plat_config.misc_flags & FCB_ENCODE_HAMMING) && + (plat_config.misc_flags & FCB_LAYOUT_RESV_12B)) { fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); if (!fcb_raw_page) { @@ -265,34 +598,36 @@ static int write_fcb_dbbt_and_readback(struct mtd_info *mtd, */ memset(fcb_raw_page + mtd->writesize, 0xFF, 2); } - for (i = 0; i < 2; i++) { + + /* start writing FCB from the very beginning */ + off = 0; + + for (i = 0; i < g_boot_search_count; i++) { if (mtd_block_isbad(mtd, off)) { printf("Block %d is bad, skipped\n", i); continue; } /* - * User BCH ECC hardware module for i.MX7 + * User BCH hardware module to generate ECC for FCB */ - if (is_mx7() || is_imx8m()) { - u32 off = i * mtd->erasesize; - size_t rwsize = sizeof(*fcb); - - printf("Writing %zd bytes to 0x%x: ", rwsize, off); + if (plat_config.misc_flags & FCB_ENCODE_BCH) { + size = sizeof(struct fcb_block); /* switch nand BCH to FCB compatible settings */ - mxs_nand_mode_fcb(mtd); - ret = nand_write(mtd, off, &rwsize, - (unsigned char *)fcb); + if (plat_config.misc_flags & FCB_ENCODE_BCH_62b) + mxs_nand_mode_fcb_62bit(mtd); + else if (plat_config.misc_flags & FCB_ENCODE_BCH_40b) + mxs_nand_mode_fcb_40bit(mtd); - dump_fcb_off[i] = off; - nand_read(mtd, off, &rwsize, - (unsigned char *)(dump_nand_fcb + i)); + ret = nand_write(mtd, off, &size, (u_char *)fcb); + /* switch BCH back */ mxs_nand_mode_normal(mtd); + printf("NAND FCB write to 0x%zx offset 0x%llx written: %s\n", + size, off, ret ? "ERROR" : "OK"); - printf("%s\n", ret ? "ERROR" : "OK"); - } else if (is_mx6()) { + } else if (plat_config.misc_flags & FCB_ENCODE_HAMMING) { /* raw write */ mtd_oob_ops_t ops = { .datbuf = (u8 *)fcb_raw_page, @@ -303,65 +638,327 @@ static int write_fcb_dbbt_and_readback(struct mtd_info *mtd, .mode = MTD_OPS_RAW }; - ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops); - if (ret) - goto fcb_raw_page_err; - debug("NAND fcb write: 0x%x offset 0x%zx written: %s\n", - mtd->erasesize * i, ops.len, ret ? - "ERROR" : "OK"); - - ops.datbuf = (u8 *)(dump_nand_fcb + i); - ops.oobbuf = ((u8 *)(dump_nand_fcb + i)) + mtd->writesize; - mtd_read_oob(mtd, mtd->erasesize * i, &ops); + ret = mtd_write_oob(mtd, off, &ops); + printf("NAND FCB write to 0x%llxx offset 0x%zx written: %s\n", off, ops.len, ret ? "ERROR" : "OK"); } - ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize, - mtd->writesize, &dummy, (void *)dbbt); if (ret) goto fcb_raw_page_err; - debug("NAND dbbt write: 0x%x offset, 0x%zx bytes written: %s\n", - mtd->erasesize * i + mtd->writesize, dummy, - ret ? "ERROR" : "OK"); - dump_dbbt_off[i] = mtd->erasesize * i + mtd->writesize; - size_t rwsize = sizeof(*dbbt); + /* next writing location */ + off += g_boot_search_stride; + } + + return 0; + +fcb_raw_page_err: + kfree(fcb_raw_page); + + return ret; +} + +/* + * return 1 - bad block + * return 0 - read successfully + * return < 0 - read failed + */ +static int read_dbbt(struct boot_config *boot_cfg, struct dbbt_block *dbbt, + void *dbbt_data_page, loff_t off) +{ + size_t size; + struct mtd_info *mtd; + loff_t to; + int ret; + + mtd = boot_cfg->mtd; + + if (mtd_block_isbad(mtd, off)) { + printf("Block %d is bad, skipped\n", + (int)CONV_TO_BLOCKS(off)); + return 1; + } + + size = sizeof(struct dbbt_block); + ret = nand_read(mtd, off, &size, (u_char *)dbbt); + printf("NAND DBBT read from 0x%llx offset 0x%zx read: %s\n", + off, size, ret ? "ERROR" : "OK"); + if (ret) + return ret; + + /* dbbtpages == 0 if no bad blocks */ + if (dbbt->dbbtpages > 0) { + to = off + 4 * mtd->writesize; + size = mtd->writesize; + ret = nand_read(mtd, to, &size, dbbt_data_page); + printf("DBBT data read from 0x%llx offset 0x%zx read: %s\n", + to, size, ret ? "ERROR" : "OK"); + + if (ret) + return ret; + } + + return 0; +} + +static int write_dbbt(struct boot_config *boot_cfg, struct dbbt_block *dbbt, + void *dbbt_data_page) +{ + int i; + loff_t off, to; + size_t size; + struct mtd_info *mtd; + int ret; + + mtd = boot_cfg->mtd; + + /* start writing DBBT after all FCBs */ + off = boot_cfg->search_area_size_in_bytes; + size = mtd->writesize; - nand_read(mtd, dump_dbbt_off[i], &rwsize, - (unsigned char *)(dump_nand_dbbt + i)); + for (i = 0; i < g_boot_search_count; i++) { + if (mtd_block_isbad(mtd, off)) { + printf("Block %d is bad, skipped\n", + (int)(i + CONV_TO_BLOCKS(off))); + continue; + } + + ret = nand_write(mtd, off, &size, (u_char *)dbbt); + printf("NAND DBBT write to 0x%llx offset 0x%zx written: %s\n", + off, size, ret ? "ERROR" : "OK"); + if (ret) + return ret; /* dbbtpages == 0 if no bad blocks */ if (dbbt->dbbtpages > 0) { - loff_t to = (mtd->erasesize * i + mtd->writesize * 5); + to = off + 4 * mtd->writesize; + ret = nand_write(mtd, to, &size, dbbt_data_page); + printf("DBBT data write to 0x%llx offset 0x%zx written: %s\n", + to, size, ret ? "ERROR" : "OK"); - ret = mtd_write(mtd, to, mtd->writesize, &dummy, - dbbt_data_page); - if (ret) - goto fcb_raw_page_err; + if (ret) + return ret; } + + /* next writing location */ + off += g_boot_search_stride; } -fcb_raw_page_err: - if (is_mx6()) - kfree(fcb_raw_page); + return 0; +} + +/* reuse the check_skip_len from nand_util.c with minor change*/ +static int check_skip_length(struct boot_config *boot_cfg, loff_t offset, + size_t length, size_t *used) +{ + struct mtd_info *mtd = boot_cfg->mtd; + size_t maxsize = boot_cfg->maxsize; + size_t len_excl_bad = 0; + int ret = 0; + + while (len_excl_bad < length) { + size_t block_len, block_off; + loff_t block_start; + + if (offset >= maxsize) + return -1; + + block_start = offset & ~(loff_t)(mtd->erasesize - 1); + block_off = offset & (mtd->erasesize - 1); + block_len = mtd->erasesize - block_off; + + if (!nand_block_isbad(mtd, block_start)) + len_excl_bad += block_len; + else + ret = 1; + + offset += block_len; + *used += block_len; + } + + /* If the length is not a multiple of block_len, adjust. */ + if (len_excl_bad > length) + *used -= (len_excl_bad - length); return ret; } -static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size, - size_t maxsize, const u_char *buf) +static int nandbcb_get_next_good_blk_addr(struct boot_config *boot_cfg, + struct boot_stream_config *bs_cfg) { + struct mtd_info *mtd = boot_cfg->mtd; + loff_t offset = bs_cfg->bs_addr; + size_t length = bs_cfg->bs_size; + size_t used = 0; + int ret; + + ret = check_skip_length(boot_cfg, offset, length, &used); + + if (ret < 0) + return ret; + + /* get next image address */ + bs_cfg->next_bs_addr = (u32)(offset + used + mtd->erasesize - 1) + / (u32)mtd->erasesize * mtd->erasesize; + + return ret; +} + +static int nandbcb_write_bs_skip_bad(struct boot_config *boot_cfg, + struct boot_stream_config *bs_cfg) +{ + struct mtd_info *mtd; + void *buf; + loff_t offset, maxsize; + size_t size; + size_t length; + int ret; + bool padding_flag = false; + + mtd = boot_cfg->mtd; + offset = bs_cfg->bs_addr; + maxsize = boot_cfg->maxsize; + size = bs_cfg->bs_size; + + /* some boot images may need leading offset */ + if (bs_cfg->need_padding && + (plat_config.misc_flags & FIRMWARE_NEED_PADDING)) + padding_flag = 1; + + if (padding_flag) + length = ALIGN(size + FLASH_OFFSET_STANDARD, mtd->writesize); + else + length = ALIGN(size, mtd->writesize); + + buf = kzalloc(length, GFP_KERNEL); + if (!buf) { + printf("failed to allocate buffer for firmware\n"); + ret = -ENOMEM; + return ret; + } + + if (padding_flag) + memcpy(buf + FLASH_OFFSET_STANDARD, bs_cfg->bs_buf, size); + else + memcpy(buf, bs_cfg->bs_buf, size); + + ret = nand_write_skip_bad(mtd, offset, &length, NULL, maxsize, + (u_char *)buf, WITH_WR_VERIFY); + printf("Write %s @0x%llx offset, 0x%zx bytes written: %s\n", + bs_cfg->bs_label, offset, length, ret ? "ERROR" : "OK"); + + if (ret) + /* write image failed, quit */ + goto err; + + /* get next good blk address if needed */ + if (bs_cfg->need_padding) { + ret = nandbcb_get_next_good_blk_addr(boot_cfg, bs_cfg); + if (ret < 0) { + printf("Next image cannot fit in NAND partition\n"); + goto err; + } + } + + /* now we know how the exact image size written to NAND */ + bs_cfg->bs_size = length; + return 0; +err: + kfree(buf); + return ret; +} + +static int nandbcb_write_fw(struct boot_config *boot_cfg, u_char *buf, + int index) +{ + int i; + loff_t offset; + size_t size; + loff_t next_bs_addr; + struct boot_stream_config bs_cfg; + int ret; + + for (i = 0; i < 2; ++i) { + if (!(FW_INX(i) & index)) + continue; + + if (i == 0) { + offset = boot_cfg->boot_stream1_address; + size = boot_cfg->boot_stream1_size; + } else { + offset = boot_cfg->boot_stream2_address; + size = boot_cfg->boot_stream2_size; + } + + /* write Firmware*/ + if (!(plat_config.misc_flags & FIRMWARE_EXTRA_ONE)) { + memset(&bs_cfg, 0, sizeof(struct boot_stream_config)); + sprintf(bs_cfg.bs_label, "firmware%d", i); + bs_cfg.bs_addr = offset; + bs_cfg.bs_size = size; + bs_cfg.bs_buf = buf; + bs_cfg.need_padding = 1; + + ret = nandbcb_write_bs_skip_bad(boot_cfg, &bs_cfg); + if (ret) + return ret; + + /* update the boot stream size */ + if (i == 0) + boot_cfg->boot_stream1_size = bs_cfg.bs_size; + else + boot_cfg->boot_stream2_size = bs_cfg.bs_size; + + } else { + /* some platforms need extra firmware */ + memset(&bs_cfg, 0, sizeof(struct boot_stream_config)); + sprintf(bs_cfg.bs_label, "fw%d_part%d", i, 1); + bs_cfg.bs_addr = offset; + bs_cfg.bs_size = IMX8MQ_HDMI_FW_SZ; + bs_cfg.bs_buf = buf; + bs_cfg.need_padding = 1; + + ret = nandbcb_write_bs_skip_bad(boot_cfg, &bs_cfg); + if (ret) + return ret; + + /* update the boot stream size */ + if (i == 0) + boot_cfg->boot_stream1_size = bs_cfg.bs_size; + else + boot_cfg->boot_stream2_size = bs_cfg.bs_size; + + /* get next image address */ + next_bs_addr = bs_cfg.next_bs_addr; + + memset(&bs_cfg, 0, sizeof(struct boot_stream_config)); + sprintf(bs_cfg.bs_label, "fw%d_part%d", i, 2); + bs_cfg.bs_addr = next_bs_addr; + bs_cfg.bs_size = IMX8MQ_SPL_SZ; + bs_cfg.bs_buf = (u_char *)(buf + IMX8MQ_HDMI_FW_SZ); + bs_cfg.need_padding = 0; + + ret = nandbcb_write_bs_skip_bad(boot_cfg, &bs_cfg); + if (ret) + return ret; + } + } + + return 0; +} + +static int nandbcb_init(struct boot_config *boot_cfg, u_char *buf) +{ + struct mtd_info *mtd; nand_erase_options_t opts; struct fcb_block *fcb; struct dbbt_block *dbbt; - loff_t fw1_off; - void *fwbuf, *dbbt_page, *dbbt_data_page; - u32 fw1_start, fw1_pages; - int nr_blks, nr_blks_fcb, fw1_blk; - size_t fwsize; + void *dbbt_page, *dbbt_data_page; int ret; - size_t extra_fwsize; - void *extra_fwbuf; - loff_t extra_fw1_off; + loff_t maxsize, off; + + mtd = boot_cfg->mtd; + maxsize = boot_cfg->maxsize; + off = boot_cfg->offset; /* erase */ memset(&opts, 0, sizeof(opts)); @@ -391,85 +988,24 @@ static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size, * - two firmware blocks, primary and secondary * - first 4 block for FCB/DBBT * - rest split in half for primary and secondary firmware - * - same firmware will write two times + * - same firmware write twice */ - nr_blks_fcb = BOOT_SEARCH_COUNT; - nr_blks = maxsize / mtd->erasesize; - fw1_blk = nr_blks_fcb; - - /* write fw */ - fwbuf = NULL; - if (is_mx6() || is_mx7()) { - fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize, - mtd->writesize); - fwbuf = kzalloc(fwsize, GFP_KERNEL); - if (!fwbuf) { - debug("failed to allocate fwbuf\n"); - ret = -ENOMEM; - goto err; - } - - memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size); - fw1_off = fw1_blk * mtd->erasesize; - ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize, - (u_char *)fwbuf, WITH_WR_VERIFY); - printf("NAND fw write: 0x%llx offset, 0x%zx bytes written: %s\n", - fw1_off, fwsize, ret ? "ERROR" : "OK"); - if (ret) - goto fwbuf_err; - } else if (is_imx8m()) { - fwsize = ALIGN(IMX8MQ_SPL_SZ + FLASH_OFFSET_STANDARD + mtd->writesize, mtd->writesize); - fwbuf = kzalloc(fwsize, GFP_KERNEL); - if (!fwbuf) { - printf("failed to allocate fwbuf\n"); - ret = -ENOMEM; - goto err; - } - memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, IMX8MQ_SPL_SZ); - fw1_off = fw1_blk * mtd->erasesize; - ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize, - (u_char *)fwbuf, WITH_WR_VERIFY); - printf("NAND fw write: 0x%llx offset, 0x%zx bytes written: %s\n", - fw1_off, fwsize, ret ? "ERROR" : "OK"); - if (ret) - goto fwbuf_err; - - extra_fwsize = ALIGN(IMX8MQ_SPL_SZ + mtd->writesize, mtd->writesize); - extra_fwbuf = kzalloc(extra_fwsize, GFP_KERNEL); - extra_fw1_off = fw1_off + mtd->erasesize * ((IMX8MQ_SPL_SZ + mtd->erasesize - 1) / mtd->erasesize); - if (!extra_fwbuf) { - printf("failed to allocate fwbuf\n"); - ret = -ENOMEM; - goto fwbuf_err; - } - - memcpy(extra_fwbuf, buf + IMX8MQ_HDMI_FW_SZ, IMX8MQ_SPL_SZ); - ret = nand_write_skip_bad(mtd, extra_fw1_off, &extra_fwsize, NULL, maxsize, - (u_char *)extra_fwbuf, WITH_WR_VERIFY); - printf("NAND extra_fw write: 0x%llx offset, 0x%zx bytes written: %s\n", - extra_fw1_off, extra_fwsize, ret ? "ERROR" : "OK"); - if (ret) { - kfree(extra_fwbuf); - goto fwbuf_err; - } - } + /* write Firmware*/ + ret = nandbcb_write_fw(boot_cfg, buf, FW_ALL); + if (ret) + goto err; /* fill fcb */ fcb = kzalloc(sizeof(*fcb), GFP_KERNEL); if (!fcb) { debug("failed to allocate fcb\n"); ret = -ENOMEM; - goto fwbuf_err; + return ret; } + fill_fcb(fcb, boot_cfg); - fw1_start = (fw1_blk * mtd->erasesize) / mtd->writesize; - fw1_pages = size / mtd->writesize + 1; - if (is_imx8m()) - fw1_pages = (IMX8MQ_SPL_SZ + (mtd->writesize - 1)) / mtd->writesize; - fill_fcb(fcb, mtd, fw1_start, 0, fw1_pages); - - dump_fill_fcb = *fcb; + ret = write_fcb(boot_cfg, fcb); /* fill dbbt */ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); @@ -488,18 +1024,16 @@ static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size, dbbt = dbbt_page; dbbt->checksum = 0; - dbbt->fingerprint = DBBT_FINGERPRINT2; + dbbt->fingerprint = DBBT_FINGERPRINT; dbbt->version = DBBT_VERSION_1; - ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks); + ret = fill_dbbt_data(mtd, dbbt_data_page, CONV_TO_BLOCKS(maxsize)); if (ret < 0) goto dbbt_data_page_err; else if (ret > 0) dbbt->dbbtpages = 1; - dump_fill_dbbt = *dbbt; - - /* write fcb and dbbt to nand */ - ret = write_fcb_dbbt_and_readback(mtd, fcb, dbbt, dbbt_data_page, off); + /* write dbbt */ + ret = write_dbbt(boot_cfg, dbbt, dbbt_data_page); if (ret < 0) printf("failed to write FCB/DBBT\n"); @@ -509,8 +1043,6 @@ dbbt_page_err: kfree(dbbt_page); fcb_err: kfree(fcb); -fwbuf_err: - kfree(fwbuf); err: return ret; } @@ -519,69 +1051,98 @@ static int do_nandbcb_bcbonly(int argc, char * const argv[]) { struct fcb_block *fcb; struct dbbt_block *dbbt; - u32 fw_len, fw1_off, fw2_off; struct mtd_info *mtd; + nand_erase_options_t opts; + size_t maxsize; + loff_t off; void *dbbt_page, *dbbt_data_page; - int dev, ret; + int ret; + struct boot_config cfg; - dev = nand_curr_device; - if ((dev < 0) || (dev >= CONFIG_SYS_MAX_NAND_DEVICE) || - (!get_nand_dev_by_index(dev))) { - puts("No devices available\n"); + if (argc < 4) + return CMD_RET_USAGE; + + memset(&cfg, 0, sizeof(struct boot_config)); + if (nandbcb_get_info(argc, argv, &cfg)) return CMD_RET_FAILURE; - } - mtd = get_nand_dev_by_index(dev); + /* only get the partition info */ + if (nandbcb_get_size(2, argv, 1, &cfg)) + return CMD_RET_FAILURE; - if (argc < 3) + if (nandbcb_set_boot_config(argc, argv, &cfg)) return CMD_RET_FAILURE; - fw_len = simple_strtoul(argv[1], NULL, 16); - fw1_off = simple_strtoul(argv[2], NULL, 16); + mtd = cfg.mtd; - if (argc > 3) - fw2_off = simple_strtoul(argv[3], NULL, 16); - else - fw2_off = fw1_off; + cfg.boot_stream1_address = simple_strtoul(argv[2], NULL, 16); + cfg.boot_stream1_size = simple_strtoul(argv[3], NULL, 16); + cfg.boot_stream1_size = ALIGN(cfg.boot_stream1_size, mtd->writesize); + + if (argc > 5) { + cfg.boot_stream2_address = simple_strtoul(argv[4], NULL, 16); + cfg.boot_stream2_size = simple_strtoul(argv[5], NULL, 16); + cfg.boot_stream2_size = ALIGN(cfg.boot_stream2_size, + mtd->writesize); + } + + /* sanity check */ + nandbcb_check_space(&cfg); + + maxsize = cfg.maxsize; + off = cfg.offset; + + /* erase the previous FCB/DBBT */ + memset(&opts, 0, sizeof(opts)); + opts.offset = off; + opts.length = g_boot_search_stride * 2; + ret = nand_erase_opts(mtd, &opts); + if (ret) { + printf("%s: erase failed (ret = %d)\n", __func__, ret); + return CMD_RET_FAILURE; + } /* fill fcb */ fcb = kzalloc(sizeof(*fcb), GFP_KERNEL); if (!fcb) { - debug("failed to allocate fcb\n"); + printf("failed to allocate fcb\n"); ret = -ENOMEM; return CMD_RET_FAILURE; } - fill_fcb(fcb, mtd, fw1_off / mtd->writesize, - fw2_off / mtd->writesize, fw_len / mtd->writesize); + fill_fcb(fcb, &cfg); + + /* write fcb */ + ret = write_fcb(&cfg, fcb); /* fill dbbt */ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL); if (!dbbt_page) { - debug("failed to allocate dbbt_page\n"); + printf("failed to allocate dbbt_page\n"); ret = -ENOMEM; goto fcb_err; } dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); if (!dbbt_data_page) { - debug("failed to allocate dbbt_data_page\n"); + printf("failed to allocate dbbt_data_page\n"); ret = -ENOMEM; goto dbbt_page_err; } dbbt = dbbt_page; dbbt->checksum = 0; - dbbt->fingerprint = DBBT_FINGERPRINT2; + dbbt->fingerprint = DBBT_FINGERPRINT; dbbt->version = DBBT_VERSION_1; - ret = dbbt_fill_data(mtd, dbbt_data_page, 0); + ret = fill_dbbt_data(mtd, dbbt_data_page, CONV_TO_BLOCKS(maxsize)); if (ret < 0) goto dbbt_data_page_err; else if (ret > 0) dbbt->dbbtpages = 1; - /* write fcb and dbbt to nand */ - ret = write_fcb_dbbt_and_readback(mtd, fcb, dbbt, dbbt_data_page, 0); + /* write dbbt */ + ret = write_dbbt(&cfg, dbbt, dbbt_data_page); + dbbt_data_page_err: kfree(dbbt_data_page); dbbt_page_err: @@ -597,49 +1158,20 @@ fcb_err: return CMD_RET_SUCCESS; } -/* dump data which is planned to be encoded and written to NAND chip */ -void mtd_cfg_dump(void) -{ - u64 blocks; - - printf("MTD CONFIG:\n"); - printf(" %s = %d\n", "data_setup_time", dump_fill_fcb.datasetup); - printf(" %s = %d\n", "data_hold_time", dump_fill_fcb.datahold); - printf(" %s = %d\n", "address_setup_time", dump_fill_fcb.addr_setup); - printf(" %s = %d\n", "data_sample_time", dump_fill_fcb.dsample_time); - - printf("NFC geometry :\n"); - printf("\tECC Strength : %d\n", dump_mtd->ecc_strength); - printf("\tPage Size in Bytes : %d\n", dump_fill_fcb.oob_pagesize); - printf("\tMetadata size : %d\n", dump_fill_fcb.meta_size); - printf("\tECC Chunk Size in byte : %d\n", dump_fill_fcb.ecc_size); - printf("\tECC Chunk count : %d\n", dump_fill_fcb.nr_blocks + 1); - printf("\tBlock Mark Byte Offset : %d\n", dump_fill_fcb.bb_byte); - printf("\tBlock Mark Bit Offset : %d\n", dump_fill_fcb.bb_start_bit); - printf("====================================================\n"); - - printf("mtd: partition #0\n"); - printf(" %s = %d\n", "type", dump_mtd->type); - printf(" %s = %d\n", "flags", dump_mtd->flags); - printf(" %s = %llu\n", "size", dump_nandboot_size); - printf(" %s = %d\n", "erasesize", dump_mtd->erasesize); - printf(" %s = %d\n", "writesize", dump_mtd->writesize); - printf(" %s = %d\n", "oobsize", dump_mtd->oobsize); - blocks = dump_nandboot_size; - do_div(blocks, dump_mtd->erasesize); - printf(" %s = %llu\n", "blocks", blocks); -} - /* dump data which is read from NAND chip */ -void mtd_dump_structure(int i) +void dump_structure(struct boot_config *boot_cfg, struct fcb_block *fcb, + struct dbbt_block *dbbt, void *dbbt_data_page) { - #define P1(x) printf(" %s = 0x%08x\n", #x, dump_nand_fcb[i].x) - printf("FCB %d:\n", i); + int i; + struct mtd_info *mtd = boot_cfg->mtd; + + #define P1(x) printf(" %s = 0x%08x\n", #x, fcb->x) + printf("FCB\n"); P1(checksum); P1(fingerprint); P1(version); #undef P1 - #define P1(x) printf(" %s = %d\n", #x, dump_nand_fcb[i].x) + #define P1(x) printf(" %s = %d\n", #x, fcb->x) P1(datasetup); P1(datahold); P1(addr_setup); @@ -684,161 +1216,206 @@ void mtd_dump_structure(int i) P1(busytimeout); P1(disbbm); P1(spare_offset); +#if !defined(CONFIG_MX6) || defined(CONFIG_MX6SX) || \ + defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) P1(onfi_sync_enable); P1(onfi_sync_speed); P1(onfi_sync_nand_data); P1(disbbm_search); P1(disbbm_search_limit); P1(read_retry_enable); +#endif #undef P1 - #define P1(x) printf(" %s = 0x%08x\n", #x, dump_nand_dbbt[i].x) - printf("DBBT %d:\n", i); + #define P1(x) printf(" %s = 0x%08x\n", #x, dbbt->x) + printf("DBBT :\n"); P1(checksum); P1(fingerprint); P1(version); #undef P1 - #define P1(x) printf(" %s = %d\n", #x, dump_nand_dbbt[i].x) - P1(numberbb); + #define P1(x) printf(" %s = %d\n", #x, dbbt->x) + P1(dbbtpages); #undef P1 - printf("Firmware: image #0 @ 0x%x size 0x%x - available 0x%llx\n", - dump_nand_fcb[i].fw1_start * dump_nand_fcb[i].pagesize, - dump_nand_fcb[i].fw1_pages * dump_nand_fcb[i].pagesize, - dump_nandboot_size - dump_nand_fcb[i].fw1_start * - dump_nand_fcb[i].pagesize); - if (is_imx8m()) { - printf("Extra Firmware: image #0 @ 0x%x size 0x%x - available 0x%llx\n", - dump_nand_fcb[i].fw1_start * - dump_nand_fcb[i].pagesize + dump_mtd->erasesize * - ((IMX8MQ_SPL_SZ + dump_mtd->erasesize - 1) / - dump_mtd->erasesize), - dump_nand_fcb[i].fw1_pages * dump_nand_fcb[i].pagesize, - dump_nandboot_size - - (dump_nand_fcb[i].fw1_start * - dump_nand_fcb[i].pagesize + dump_mtd->erasesize * - ((IMX8MQ_SPL_SZ + dump_mtd->erasesize - 1) / - dump_mtd->erasesize))); + for (i = 0; i < dbbt->dbbtpages; ++i) + printf("%d ", *((u32 *)(dbbt_data_page + i))); + + if (!(plat_config.misc_flags & FIRMWARE_EXTRA_ONE)) { + printf("Firmware: image #0 @ 0x%x size 0x%x\n", + fcb->fw1_start, fcb->fw1_pages * mtd->writesize); + printf("Firmware: image #1 @ 0x%x size 0x%x\n", + fcb->fw2_start, fcb->fw2_pages * mtd->writesize); + } else { + printf("Firmware: image #0 @ 0x%x size 0x%x\n", + fcb->fw1_start, fcb->fw1_pages * mtd->writesize); + printf("Firmware: image #1 @ 0x%x size 0x%x\n", + fcb->fw2_start, fcb->fw2_pages * mtd->writesize); + /* TODO: Add extra image information */ } } -static int do_nandbcb_dump(int argc, char * const argv[]) +static bool check_fingerprint(void *data, int fingerprint) { - int num; - int stride; - int search_area_sz; - bool bab_block_table[BOOT_SEARCH_COUNT]; - int bab_block_flag; + int off = 4; - if (argc != 2) - return CMD_RET_USAGE; + return (*(int *)(data + off) == fingerprint); +} - switch (argv[1][0]) { - case '0': - num = 0; - break; - case '1': - num = 1; - break; - default: - return CMD_RET_USAGE; - } +static int nandbcb_dump(struct boot_config *boot_cfg) +{ + int i; + loff_t off; + struct mtd_info *mtd = boot_cfg->mtd; + struct fcb_block fcb, fcb_copy; + struct dbbt_block dbbt, dbbt_copy; + void *dbbt_data_page, *dbbt_data_page_copy; + bool fcb_not_found, dbbt_not_found; + int ret = 0; - /* dump data which is planned to be encoded and written to NAND chip */ - mtd_cfg_dump(); + dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_data_page) { + printf("failed to allocate dbbt_data_page\n"); + ret = -ENOMEM; + return ret; + } - stride = dump_mtd->erasesize; - search_area_sz = BOOT_SEARCH_COUNT * stride; - printf("stride: %x, search_area_sz: %x\n", stride, search_area_sz); + dbbt_data_page_copy = kzalloc(mtd->writesize, GFP_KERNEL); + if (!dbbt_data_page_copy) { + printf("failed to allocate dbbt_data_page\n"); + ret = -ENOMEM; + goto dbbt_page_err; + } - bab_block_flag = 0; - for (int i = 0; i < BOOT_SEARCH_COUNT; i++) { - if (mtd_block_isbad(dump_mtd, - (loff_t)(dump_mtd->erasesize * i))) { - bab_block_table[i] = 1; - bab_block_flag = 1; - continue; - } - bab_block_table[i] = 0; - if (!memcmp(dump_nand_fcb + i, &dump_fill_fcb, - sizeof(dump_fill_fcb))) { - printf("mtd: found FCB%d candidate version %08x @%d:0x%x\n", - i, dump_nand_fcb[i].version, i, dump_fcb_off[i]); + /* read fcb */ + fcb_not_found = 1; + off = 0; + for (i = 0; i < g_boot_search_count; ++i) { + if (fcb_not_found) { + ret = read_fcb(boot_cfg, &fcb, off); + + if (ret < 0) + goto dbbt_page_copy_err; + else if (ret == 1) + continue; + else if (ret == 0) + if (check_fingerprint(&fcb, FCB_FINGERPRINT)) + fcb_not_found = 0; } else { - printf("mtd: FCB%d not found\n", i); + ret = read_fcb(boot_cfg, &fcb_copy, off); + + if (ret < 0) + goto dbbt_page_copy_err; + if (memcmp(&fcb, &fcb_copy, + sizeof(struct fcb_block))) { + printf("FCB copies are not identical\n"); + ret = -EINVAL; + goto dbbt_page_copy_err; + } } - } - - for (int i = 0; i < BOOT_SEARCH_COUNT; i++) { - if (mtd_block_isbad(dump_mtd, - (loff_t)(dump_mtd->erasesize * i))) - continue; - if (!memcmp(dump_nand_dbbt + i, &dump_fill_dbbt, - sizeof(dump_fill_dbbt))) { - printf("mtd: DBBT%d found\n", i); - printf("mtd: Valid DBBT%d found @%d:0x%x\n", - i, i, dump_dbbt_off[i]); + /* next read location */ + off += g_boot_search_stride; + } + /* read dbbt*/ + dbbt_not_found = 1; + off = boot_cfg->search_area_size_in_bytes; + for (i = 0; i < g_boot_search_count; ++i) { + if (dbbt_not_found) { + ret = read_dbbt(boot_cfg, &dbbt, dbbt_data_page, off); + + if (ret < 0) + goto dbbt_page_copy_err; + else if (ret == 1) + continue; + else if (ret == 0) + if (check_fingerprint(&dbbt, DBBT_FINGERPRINT)) + dbbt_not_found = 0; } else { - printf("mtd: DBBT%d not found\n", i); - } - } - if (bab_block_flag == 0) { - printf("no bad block found, dbbt: %08x\n", - dump_fill_dbbt.fingerprint); - } else { - for (int i = 0; i < BOOT_SEARCH_COUNT; i++) { - if (bab_block_table[i] == 1) - printf("mtd: bad block @ 0x%llx\n", - (loff_t)(dump_mtd->erasesize * i)); + ret = read_dbbt(boot_cfg, &dbbt_copy, + dbbt_data_page_copy, off); + + if (ret < 0) + goto dbbt_page_copy_err; + if (memcmp(&dbbt, &dbbt_copy, + sizeof(struct dbbt_block))) { + printf("DBBT copies are not identical\n"); + ret = -EINVAL; + goto dbbt_page_copy_err; + } + if (dbbt.dbbtpages > 0 && + memcmp(dbbt_data_page, dbbt_data_page_copy, + mtd->writesize)) { + printf("DBBT data copies are not identical\n"); + ret = -EINVAL; + goto dbbt_page_copy_err; + } } + + /* next read location */ + off += g_boot_search_stride; } - /* dump data which is read from NAND chip */ - if (num > (BOOT_SEARCH_COUNT - 1)) - return CMD_RET_USAGE; + dump_structure(boot_cfg, &fcb, &dbbt, dbbt_data_page); + +dbbt_page_copy_err: + kfree(dbbt_data_page_copy); +dbbt_page_err: + kfree(dbbt_data_page); + + return ret; +} + +static int do_nandbcb_dump(int argc, char * const argv[]) +{ + struct boot_config cfg; + int ret; - if (bab_block_table[num] == 1) { - printf("mtd: bad block @ 0x%llx (FCB - DBBT)\n", - (loff_t)(dump_mtd->erasesize * num)); + if (argc != 2) return CMD_RET_USAGE; - } - mtd_dump_structure(num); + memset(&cfg, 0, sizeof(struct boot_config)); + if (nandbcb_get_info(argc, argv, &cfg)) + return CMD_RET_FAILURE; + + if (nandbcb_get_size(argc, argv, 1, &cfg)) + return CMD_RET_FAILURE; - return 0; + if (nandbcb_set_boot_config(argc, argv, &cfg)) + return CMD_RET_FAILURE; + + ret = nandbcb_dump(&cfg); + if (ret) + return ret; + + return ret; } -static int do_nandbcb_update(int argc, char * const argv[]) +static int do_nandbcb_init(int argc, char * const argv[]) { - struct mtd_info *mtd; - loff_t addr, offset, size, maxsize; - char *endp; u_char *buf; - int dev; + size_t size; + loff_t addr; + char *endp; int ret; + struct boot_config cfg; if (argc != 4) return CMD_RET_USAGE; - dev = nand_curr_device; - if (dev < 0) { - printf("failed to get nand_curr_device, run nand device\n"); + memset(&cfg, 0, sizeof(struct boot_config)); + if (nandbcb_get_info(argc, argv, &cfg)) return CMD_RET_FAILURE; - } - addr = simple_strtoul(argv[1], &endp, 16); - if (*argv[1] == 0 || *endp != 0) + if (nandbcb_get_size(argc, argv, 2, &cfg)) return CMD_RET_FAILURE; + size = cfg.boot_stream1_size; - mtd = get_nand_dev_by_index(dev); - if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size, - &maxsize, MTD_DEV_TYPE_NAND, mtd->size)) + if (nandbcb_set_boot_config(argc, argv, &cfg)) return CMD_RET_FAILURE; - /* dump_mtd and dump_nandboot_size are used for "nandbcb dump [-v]" */ - dump_mtd = mtd; - dump_nandboot_size = maxsize; + addr = simple_strtoul(argv[1], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return CMD_RET_FAILURE; buf = map_physmem(addr, size, MAP_WRBACK); if (!buf) { @@ -846,7 +1423,7 @@ static int do_nandbcb_update(int argc, char * const argv[]) return CMD_RET_FAILURE; } - ret = nandbcb_update(mtd, offset, size, maxsize, buf); + ret = nandbcb_init(&cfg, buf); return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE; } @@ -860,12 +1437,36 @@ static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc, if (argc < 3) goto usage; + /* check the platform config first */ + if (is_mx6sx()) { + plat_config = imx6sx_plat_config; + } else if (is_mx7()) { + plat_config = imx7d_plat_config; + } else if (is_mx6ul() || is_mx6ull()) { + plat_config = imx6ul_plat_config; + } else if (is_mx6() && !is_mx6sx() && !is_mx6ul() && !is_mx6ull()) { + plat_config = imx6qdl_plat_config; + } else if (is_imx8mq()) { + plat_config = imx8mq_plat_config; + } else if (is_imx8mm()) { + plat_config = imx8mm_plat_config; + } else if (is_imx8mn()) { + plat_config = imx8mn_plat_config; + } else if (is_imx8qm() || is_imx8qxp()) { + plat_config = imx8q_plat_config; + } else { + printf("ERROR: Unknown platform\n"); + return CMD_RET_FAILURE; + } + + /* TODO: set the boot search count if need to read from fuse */ + cmd = argv[1]; --argc; ++argv; - if (strcmp(cmd, "update") == 0) { - ret = do_nandbcb_update(argc, argv); + if (strcmp(cmd, "init") == 0) { + ret = do_nandbcb_init(argc, argv); goto done; } @@ -888,19 +1489,19 @@ usage: #ifdef CONFIG_SYS_LONGHELP static char nandbcb_help_text[] = - "update addr off|partition len - update 'len' bytes starting at\n" + "init addr off|partition len - update 'len' bytes starting at\n" " 'off|part' to memory address 'addr', skipping bad blocks\n" - "bcbonly fw-size fw1-off [fw2-off] - write only BCB (FCB and DBBT)\n" - " where `fw-size` is fw sizes in bytes, `fw1-off`\n" + "nandbcb bcbonly off|partition fw1-off fw1-size [fw2-off fw2-size]\n" + " - write BCB only (FCB and DBBT)\n" + " where `fwx-size` is fw sizes in bytes, `fw1-off`\n" " and `fw2-off` - firmware offsets\n" " FIY, BCB isn't erased automatically, so mtd erase should\n" " be called in advance before writing new BCB:\n" " > mtd erase mx7-bcb\n" - "nandbcb dump num - verify/dump boot structures\n" - " 'num' can be set to 0 and 1"; + "nandbcb dump off|partition - dump/verify boot structures\n"; #endif -U_BOOT_CMD(nandbcb, 5, 1, do_nandbcb, - "i.MX6/i.MX7 NAND Boot Control Blocks write", +U_BOOT_CMD(nandbcb, 7, 1, do_nandbcb, + "i.MX NAND Boot Control Blocks write", nandbcb_help_text ); |