[PATCH 3/3] cmd: mtd: add nand_read_test command support
Michael Nazzareno Trimarchi
michael at amarulasolutions.com
Sat Sep 6 00:27:04 CEST 2025
Hi
On Thu, Sep 4, 2025 at 4:50 PM Mikhail Kshevetskiy
<mikhail.kshevetskiy at iopsys.eu> wrote:
>
> This patch implements read-only test of nand flash devices.
>
> Test reads blocks of NAND flash in normal and raw modes and compares
> results. The following statuses can be returned for a block:
> * non-ecc reading failed,
> * ecc reading failed,
> * block is bad,
> * bitflips is above maximum,
> * actual number of biflips above reported one,
> * bitflips reached it maximum value,
> * block is ok.
>
> Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
> ---
> cmd/Kconfig | 16 +++++
> cmd/mtd.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 213 insertions(+)
>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index cd1a664f34f..21d6db57628 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1533,6 +1533,22 @@ config CMD_MTD_NAND_WRITE_TEST
>
> WARNING: This test will destroy any data on blocks being tested.
>
> +config CMD_MTD_NAND_READ_TEST
> + bool "mtd nand_read_test"
> + depends on CMD_MTD
> + help
> + MTD nand_read_test command support.
> +
> + Reads blocks of NAND flash in normal and raw modes and compares results.
> + The following statuses can be returned for a block:
> + * non-ecc reading failed,
> + * ecc reading failed,
> + * block is bad,
> + * bitflips is above maximum,
> + * actual number of biflips above reported one,
> + * bitflips reached it maximum value,
> + * block is ok.
> +
> config CMD_MUX
> bool "mux"
> depends on MULTIPLEXER
> diff --git a/cmd/mtd.c b/cmd/mtd.c
> index 4d69c7b4915..cee3a7e43ef 100644
> --- a/cmd/mtd.c
> +++ b/cmd/mtd.c
> @@ -951,6 +951,195 @@ out_put_mtd:
> }
> #endif
>
> +#ifdef CONFIG_CMD_MTD_NAND_READ_TEST
> +enum nand_read_status {
> + NAND_READ_STATUS_UNKNOWN = 0,
> + NAND_READ_STATUS_NONECC_READ_FAIL,
> + NAND_READ_STATUS_ECC_READ_FAIL,
> + NAND_READ_STATUS_BAD_BLOCK,
> + NAND_READ_STATUS_BITFLIP_ABOVE_MAX,
> + NAND_READ_STATUS_BITFLIP_MISMATCH,
> + NAND_READ_STATUS_BITFLIP_MAX,
> + NAND_READ_STATUS_OK,
> +};
> +
> +static enum nand_read_status nand_read_block_check(struct mtd_info *mtd,
> + loff_t off, size_t blocksize)
> +{
> + struct mtd_oob_ops ops = {};
> + u_char *buf;
> + int i, d, ret, len, pos, cnt, max;
> +
> + if (blocksize % mtd->writesize != 0) {
> + printf("\r block at 0x%llx: bad block size\n", off);
> + return NAND_READ_STATUS_UNKNOWN;
> + }
> +
> + buf = malloc_cache_aligned(2 * blocksize);
> + if (buf == NULL) {
> + printf("\r block at 0x%llx: can't allocate memory\n", off);
> + return NAND_READ_STATUS_UNKNOWN;
> + }
> +
I think that you can allocate those and check outside the function
only one time. Then you
can use standard return ENOMEM.
> + ops.mode = MTD_OPS_RAW;
> + ops.len = blocksize;
> + ops.datbuf = buf;
> + ops.ooblen = 0;
> + ops.oobbuf = NULL;
> +
You don't need to set to 0 and null and if you drop the allocation and
the check then
you can initialize in the struct definition.
> + if (mtd->_read_oob)
> + ret = mtd->_read_oob(mtd, off, &ops);
> + else
> + ret = mtd->_read(mtd, off, ops.len, &ops.retlen, ops.datbuf);
> +
> + if (ret != 0) {
> + free(buf);
> + printf("\r block at 0x%llx: non-ecc reading error %d\n",
> + off, ret);
> + return NAND_READ_STATUS_NONECC_READ_FAIL;
> + }
> +
> + ops.mode = MTD_OPS_AUTO_OOB;
> + ops.datbuf = buf + blocksize;
> +
> + if (mtd->_read_oob)
> + ret = mtd->_read_oob(mtd, off, &ops);
> + else
> + ret = mtd->_read(mtd, off, ops.len, &ops.retlen, ops.datbuf);
> +
> + if (ret == -EBADMSG) {
> + free(buf);
> + printf("\r block at 0x%llx: bad block\n", off);
> + return NAND_READ_STATUS_BAD_BLOCK;
> + }
> +
> + if (ret < 0) {
> + free(buf);
> + printf("\r block at 0x%llx: ecc reading error %d\n", off, ret);
> + return NAND_READ_STATUS_ECC_READ_FAIL;
> + }
> +
> + if (mtd->ecc_strength == 0) {
> + free(buf);
> + return NAND_READ_STATUS_OK;
> + }
> +
> + if (ret > mtd->ecc_strength) {
> + free(buf);
> + printf("\r block at 0x%llx: returned bit-flips value %d "
> + "is above maximum value %d\n",
> + off, ret, mtd->ecc_strength);
> + return NAND_READ_STATUS_BITFLIP_ABOVE_MAX;
> + }
> +
> + max = 0;
> + pos = 0;
> + len = blocksize;
> + while (len > 0) {
> + cnt = 0;
> + for (i = 0; i < mtd->ecc_step_size; i++) {
> + d = buf[pos + i] ^ buf[blocksize + pos + i];
> + if (d == 0)
> + continue;
> +
> + while (d > 0) {
> + d &= (d - 1);
> + cnt++;
> + }
> + }
> + if (cnt > max)
> + max = cnt;
> +
> + len -= mtd->ecc_step_size;
> + pos += mtd->ecc_step_size;
> + }
> +
> + free(buf);
> +
> + if (max > ret) {
> + printf("\r block at 0x%llx: bitflip mismatch, "
> + "read %d but actual %d\n", off, ret, max);
> + return NAND_READ_STATUS_BITFLIP_MISMATCH;
> + }
> +
> + if (ret == mtd->ecc_strength) {
> + printf("\r block at 0x%llx: max bitflip reached, "
> + "block is unreliable\n", off);
> + return NAND_READ_STATUS_BITFLIP_MAX;
> + }
> +
> + return NAND_READ_STATUS_OK;
> +}
> +
> +static int do_mtd_nand_read_test(struct cmd_tbl *cmdtp, int flag, int argc,
> + char *const argv[])
> +{
> + struct mtd_info *mtd;
> + u64 off, blocks;
> + int stat[NAND_READ_STATUS_OK + 1];
> + enum nand_read_status ret;
> +
> + if (argc < 2)
> + return CMD_RET_USAGE;
> +
> + mtd = get_mtd_by_name(argv[1]);
> + if (IS_ERR_OR_NULL(mtd))
> + return CMD_RET_FAILURE;
> +
> + if (!mtd_can_have_bb(mtd)) {
> + printf("Only NAND-based devices can be checked\n");
> + goto out_put_mtd;
> + }
> +
> + blocks = mtd->size;
> + do_div(blocks, mtd->erasesize);
> +
> + printf("ECC strength: %d\n", mtd->ecc_strength);
> + printf("ECC step size: %d\n", mtd->ecc_step_size);
> + printf("Erase block size: 0x%x\n", mtd->erasesize);
> + printf("Total blocks: %lld\n", blocks);
> +
> + printf("\nworking...\n");
> + memset(stat, 0, sizeof(stat));
> + for (off = 0; off < mtd->size; off += mtd->erasesize) {
> + ret = nand_read_block_check(mtd, off, mtd->erasesize);
> + stat[ret]++;
> +
> + switch (ret) {
> + case NAND_READ_STATUS_BAD_BLOCK:
> + case NAND_READ_STATUS_BITFLIP_MAX:
> + if (!mtd_block_isbad(mtd, off))
> + printf("\r block at 0x%llx: should be marked "
> + "as BAD\n", off);
> + break;
> +
> + case NAND_READ_STATUS_OK:
> + if (mtd_block_isbad(mtd, off))
> + printf("\r block at 0x%llx: marked as BAD, but "
> + "probably is GOOD\n", off);
> + break;
> +
> + default:
> + break;
> + }
> + }
> +
> +out_put_mtd:
> + put_mtd_device(mtd);
> + printf("\n");
> + printf("results:\n");
> + printf(" Good blocks: %d\n", stat[NAND_READ_STATUS_OK]);
> + printf(" Physically bad blocks: %d\n", stat[NAND_READ_STATUS_BAD_BLOCK]);
> + printf(" Unreliable blocks: %d\n", stat[NAND_READ_STATUS_BITFLIP_MAX]);
> + printf(" Non checked blocks: %d\n", stat[NAND_READ_STATUS_UNKNOWN]);
> + printf(" Failed to check blocks: %d\n", stat[NAND_READ_STATUS_NONECC_READ_FAIL] +
> + stat[NAND_READ_STATUS_ECC_READ_FAIL]);
> + printf(" Suspictious blocks: %d\n", stat[NAND_READ_STATUS_BITFLIP_ABOVE_MAX] +
> + stat[NAND_READ_STATUS_BITFLIP_MISMATCH]);
> + return CMD_RET_SUCCESS;
> +}
> +#endif
> +
> static int do_mtd_bad(struct cmd_tbl *cmdtp, int flag, int argc,
> char *const argv[])
> {
> @@ -1039,6 +1228,9 @@ U_BOOT_LONGHELP(mtd,
> #endif
> #if CONFIG_IS_ENABLED(CMD_MTD_NAND_WRITE_TEST)
> "mtd nand_write_test <name> [<off> [<size>]]\n"
> +#endif
> +#if CONFIG_IS_ENABLED(CMD_MTD_NAND_READ_TEST)
> + "mtd nand_read_test <name>\n"
> #endif
> "\n"
> "With:\n"
> @@ -1081,6 +1273,11 @@ U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
> U_BOOT_SUBCMD_MKENT_COMPLETE(nand_write_test, 4, 0,
> do_nand_write_test,
> mtd_name_complete),
> +#endif
> +#if CONFIG_IS_ENABLED(CMD_MTD_NAND_READ_TEST)
> + U_BOOT_SUBCMD_MKENT_COMPLETE(nand_read_test, 2, 0,
> + do_mtd_nand_read_test,
> + mtd_name_complete),
> #endif
> U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
> mtd_name_complete));
> --
> 2.50.1
>
Otherwise:
Reviewed-by: Michael Trimarchi <michael at amarulasolutions.com>
--
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael at amarulasolutions.com
__________________________________
Amarula Solutions BV
Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
T. +31 (0)85 111 9172
info at amarulasolutions.com
www.amarulasolutions.com
More information about the U-Boot
mailing list