Hi On Thu, Sep 4, 2025 at 4:50 PM Mikhail Kshevetskiy <mikhail.kshevets...@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.kshevets...@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 <mich...@amarulasolutions.com> -- Michael Nazzareno Trimarchi Co-Founder & Chief Executive Officer M. +39 347 913 2170 mich...@amarulasolutions.com __________________________________ Amarula Solutions BV Joop Geesinkweg 125, 1114 AB, Amsterdam, NL T. +31 (0)85 111 9172 i...@amarulasolutions.com www.amarulasolutions.com