[RESEND PATCH v2 2/3] cmd: mtd: add torture command support
Heinrich Schuchardt
xypron.glpk at gmx.de
Tue Sep 10 04:49:52 CEST 2024
On 9/10/24 00:21, Mikhail Kshevetskiy wrote:
> Some nand flashes (like spi-nand one) are registered with mtd
> subsystem only, thus nand command can't be used to work with
> such flashes. As result some functionality is missing.
>
> This patch implements 'nand torture' functionality for mtd command.
Can we call this 'nand writetest' and the test in patch 3/3 'nand readtest'?
When I read 'torture' I thought of a test running until the NAND flash
is worn out.
Please, provide a man-page for the command in doc/usage/cmd/nand.rst
Best regards
Heinrich
>
> Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
> ---
> cmd/Kconfig | 6 ++
> cmd/mtd.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 202 insertions(+)
>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 53ebafcb610..698e0e697f2 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1439,6 +1439,12 @@ config CMD_MTD_MARKBAD
> help
> MTD markbad command support.
>
> +config CMD_MTD_TORTURE
> + bool "mtd torture"
> + depends on CMD_MTD
> + help
> + MTD torture command support.
> +
> config CMD_MUX
> bool "mux"
> depends on MULTIPLEXER
> diff --git a/cmd/mtd.c b/cmd/mtd.c
> index 45084d39d3a..69ddfe1d9c6 100644
> --- a/cmd/mtd.c
> +++ b/cmd/mtd.c
> @@ -18,6 +18,7 @@
> #include <mtd.h>
> #include <dm/devres.h>
> #include <linux/err.h>
> +#include <memalign.h>
>
> #include <linux/ctype.h>
>
> @@ -742,6 +743,194 @@ out_put_mtd:
> }
> #endif
>
> +#ifdef CONFIG_CMD_MTD_TORTURE
> +/**
> + * nand_check_pattern:
> + *
> + * Check if buffer contains only a certain byte pattern.
> + *
> + * @param buf buffer to check
> + * @param patt the pattern to check
> + * @param size buffer size in bytes
> + * Return: 1 if there are only patt bytes in buf
> + * 0 if something else was found
> + */
> +static int nand_check_pattern(const u_char *buf, u_char patt, int size)
> +{
> + int i;
> +
> + for (i = 0; i < size; i++)
> + if (buf[i] != patt)
> + return 0;
> + return 1;
> +}
> +
> +/**
> + * nand_torture:
> + *
> + * Torture a block of NAND flash.
> + * This is useful to determine if a block that caused a write error is still
> + * good or should be marked as bad.
> + *
> + * @param mtd nand mtd instance
> + * @param offset offset in flash
> + * Return: 0 if the block is still good
> + */
> +static int nand_torture(struct mtd_info *mtd, loff_t offset)
> +{
> + u_char patterns[] = {0xa5, 0x5a, 0x00};
> + struct erase_info instr = {
> + .mtd = mtd,
> + .addr = offset,
> + .len = mtd->erasesize,
> + };
> + size_t retlen;
> + int err, ret = -1, i, patt_count;
> + u_char *buf;
> +
> + if ((offset & (mtd->erasesize - 1)) != 0) {
> + puts("Attempt to torture a block at a non block-aligned offset\n");
> + return -EINVAL;
> + }
> +
> + if (offset + mtd->erasesize > mtd->size) {
> + puts("Attempt to torture a block outside the flash area\n");
> + return -EINVAL;
> + }
> +
> + patt_count = ARRAY_SIZE(patterns);
> +
> + buf = malloc_cache_aligned(mtd->erasesize);
> + if (buf == NULL) {
> + puts("Out of memory for erase block buffer\n");
> + return -ENOMEM;
> + }
> +
> + for (i = 0; i < patt_count; i++) {
> + err = mtd_erase(mtd, &instr);
> + if (err) {
> + printf("%s: erase() failed for block at 0x%llx: %d\n",
> + mtd->name, instr.addr, err);
> + goto out;
> + }
> +
> + /* Make sure the block contains only 0xff bytes */
> + err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf);
> + if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) {
> + printf("%s: read() failed for block at 0x%llx: %d\n",
> + mtd->name, instr.addr, err);
> + goto out;
> + }
> +
> + err = nand_check_pattern(buf, 0xff, mtd->erasesize);
> + if (!err) {
> + printf("Erased block at 0x%llx, but a non-0xff byte was found\n",
> + offset);
> + ret = -EIO;
> + goto out;
> + }
> +
> + /* Write a pattern and check it */
> + memset(buf, patterns[i], mtd->erasesize);
> + err = mtd_write(mtd, offset, mtd->erasesize, &retlen, buf);
> + if (err || retlen != mtd->erasesize) {
> + printf("%s: write() failed for block at 0x%llx: %d\n",
> + mtd->name, instr.addr, err);
> + goto out;
> + }
> +
> + err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf);
> + if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) {
> + printf("%s: read() failed for block at 0x%llx: %d\n",
> + mtd->name, instr.addr, err);
> + goto out;
> + }
> +
> + err = nand_check_pattern(buf, patterns[i], mtd->erasesize);
> + if (!err) {
> + printf("Pattern 0x%.2x checking failed for block at "
> + "0x%llx\n", patterns[i], offset);
> + ret = -EIO;
> + goto out;
> + }
> + }
> +
> + ret = 0;
> +
> +out:
> + free(buf);
> + return ret;
> +}
> +
> +static int do_mtd_torture(struct cmd_tbl *cmdtp, int flag, int argc,
> + char *const argv[])
> +{
> + struct mtd_info *mtd;
> + loff_t off, len;
> + int ret = 0;
> + unsigned int failed = 0, passed = 0;
> +
> + 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 tortured\n");
> + goto out_put_mtd;
> + }
> +
> + argc -= 2;
> + argv += 2;
> +
> + off = argc > 0 ? hextoul(argv[0], NULL) : 0;
> + len = argc > 1 ? hextoul(argv[1], NULL) : mtd->size;
> +
> + if (!mtd_is_aligned_with_block_size(mtd, off)) {
> + printf("Offset not aligned with a block (0x%x)\n",
> + mtd->erasesize);
> + ret = CMD_RET_FAILURE;
> + goto out_put_mtd;
> + }
> +
> + if (!mtd_is_aligned_with_block_size(mtd, len)) {
> + printf("Size not a multiple of a block (0x%x)\n",
> + mtd->erasesize);
> + ret = CMD_RET_FAILURE;
> + goto out_put_mtd;
> + }
> +
> + printf("\nNAND torture: device '%s' offset 0x%llx size 0x%llx (block size 0x%x)\n",
> + mtd->name, off, len, mtd->erasesize);
> + while (len > 0) {
> + printf("\r block at %llx ", off);
> + if (mtd_block_isbad(mtd, off)) {
> + printf("marked bad, skipping\n");
> + } else {
> + ret = nand_torture(mtd, off);
> + if (ret) {
> + failed++;
> + printf("failed\n");
> + } else {
> + passed++;
> + }
> + }
> + off += mtd->erasesize;
> + len -= mtd->erasesize;
> + }
> + printf("\n Passed: %u, failed: %u\n", passed, failed);
> + if (failed != 0)
> + ret = CMD_RET_FAILURE;
> +
> +out_put_mtd:
> + put_mtd_device(mtd);
> +
> + return ret;
> +}
> +#endif
> +
> static int do_mtd_bad(struct cmd_tbl *cmdtp, int flag, int argc,
> char *const argv[])
> {
> @@ -827,6 +1016,9 @@ U_BOOT_LONGHELP(mtd,
> #endif
> #ifdef CONFIG_CMD_MTD_MARKBAD
> "mtd markbad <name> <off> [<off> ...]\n"
> +#endif
> +#ifdef CONFIG_CMD_MTD_TORTURE
> + "mtd torture <name> [<off> [<size>]]\n"
> #endif
> "\n"
> "With:\n"
> @@ -864,6 +1056,10 @@ U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
> #ifdef CONFIG_CMD_MTD_MARKBAD
> U_BOOT_SUBCMD_MKENT_COMPLETE(markbad, 20, 0, do_mtd_markbad,
> mtd_name_complete),
> +#endif
> +#ifdef CONFIG_CMD_MTD_TORTURE
> + U_BOOT_SUBCMD_MKENT_COMPLETE(torture, 4, 0, do_mtd_torture,
> + mtd_name_complete),
> #endif
> U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
> mtd_name_complete));
More information about the U-Boot
mailing list