[ Linux Busybox ] flash_eraseall command analysis

Article directory

    • Related structures
    • flash_eraseall function implementation
    • flash_eraseall implementation flow chart

File path: busybox-1.20.2/miscutils/flash_eraseall.c

Related structure

MTD related information structure

struct mtd_info_user {<!-- -->
    __u8 type; // MTD device type
    __u32 flags; // MTD device attribute flags
    __u32 size; //The size of the mtd device
    __u32 erasesize; //The erase unit size of the MTD device, for NandFlash is the size of the Block
    __u32 writesize; //The read and write unit size of the MTD device, for NandFlash is the page size
    __u32 oobsize; // oob area size
    __u64 padding; // Effective oob area size
};

flash_eraseall function implementation

If the kernel is located in the second partition, mtd2, the command used when erasing the partition is: ./flash_eraseall /dev/mtd2

int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
{<!-- -->
    struct jffs2_unknown_node cleanmarker;
    mtd_info_t meminfo;
    int fd, clmpos, clmlen;
    erase_info_t erase;
    struct stat st;
    unsigned int flags;
    char *mtd_name;

    opt_complementary = "=1";
    flags = BBTEST | getopt32(argv, "jq"); // Get the parameters in the command line

    mtd_name = argv[optind];
    fd = xopen(mtd_name, O_RDWR); // Open device /dev/mtd2
    fstat(fd, & amp;st);
    if (!S_ISCHR(st.st_mode)) // Determine whether device /dev/mtd2 is a character device
        bb_error_msg_and_die("%s: not a char device", mtd_name);

    xioctl(fd, MEMGETINFO, & amp;meminfo); // Get memory information, see structure 1
    erase.length = meminfo.erasesize;
    if (meminfo.type == MTD_NANDFLASH)
        flags |= IS_NAND;

    clmpos = 0;
    clmlen = 8;
    if (flags & amp; OPTION_J) {<!-- --> // jffs2 format partition
        uint32_t *crc32_table;

        crc32_table = crc32_filltable(NULL, 0);

        cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
        cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
        if (!(flags & amp; IS_NAND))
            cleanmarker.totlen = cpu_to_je32(sizeof(struct jffs2_unknown_node));
        else {<!-- -->
            struct nand_oobinfo oobinfo;

            xioctl(fd, MEMGETOOBSEL, & amp;oobinfo);

            /* Check for autoplacement */
            if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {<!-- -->
                /* Get the position of the free bytes */
                clmpos = oobinfo.oobfree[0][0];
                clmlen = oobinfo.oobfree[0][1];
                if (clmlen > 8)
                    clmlen = 8;
                if (clmlen == 0)
                    bb_error_msg_and_die("autoplacement selected and no empty space in oob");
            } else {<!-- -->
                /* Legacy mode */
                switch (meminfo.oobsize) {<!-- -->
                case 8:
                    clmpos = 6;
                    clmlen = 2;
                    break;
                case 16:
                    clmpos = 8;
                    /*clmlen = 8;*/
                    break;
                case 64:
                    clmpos = 16;
                    /*clmlen = 8;*/
                    break;
                }
            }
            cleanmarker.totlen = cpu_to_je32(8);
        }

        cleanmarker.hdr_crc = cpu_to_je32(
            crc32_block_endian0(0, & cleanmarker, sizeof(struct jffs2_unknown_node) - 4, crc32_table)
        );
    }

    /* Don't want to destroy progress indicator by bb_error_msg's */
    applet_name = xasprintf("\\
%s: %s", applet_name, mtd_name);

    for (erase.start = 0; erase.start < meminfo.size; // Erase flash chip in a loop, erase according to block size meminfo.erasesize
         erase.start + = meminfo.erasesize) {<!-- -->
        if (flags & amp; BBTEST) {<!-- -->
            int ret;
            loff_t offset = erase.start;

            ret = ioctl(fd, MEMGETBADBLOCK, & amp;offset); //Use offset to determine whether the offset is a bad block
            if (ret > 0) {<!-- -->
                if (!(flags & amp; OPTION_Q)) // Determine whether it is silent mode (no information printed)
                    bb_info_msg("\\
Skipping bad block at 0x x", erase.start);
                continue;
            }
            if (ret < 0) {<!-- --> // The block table is not available for some flash types, such as NOR
                /* Black block table is not available on certain flash
                 * types e.g. NOR
                 */
                if (errno == EOPNOTSUPP) {<!-- -->
                    flags &= ~BBTEST;
                    if (flags & amp; IS_NAND)
                        bb_error_msg_and_die("bad block check not available");
                } else {<!-- -->
                    bb_perror_msg_and_die("MEMGETBADBLOCK error");
                }
            }
        }
        //Update erasure progress bar
        if (!(flags & amp; OPTION_Q))
            show_progress( & amp;meminfo, & amp;erase);

        //Block erase operation
        xioctl(fd, MEMERASE, & amp;erase);

        /* format for JFFS2 ? */
        if (!(flags & amp; OPTION_J))
            continue;

        /* write cleanmarker */
        if (flags & amp; IS_NAND) {<!-- -->
            struct mtd_oob_buf oob;

            oob.ptr = (unsigned char *) & amp;cleanmarker;
            oob.start = erase.start + clmpos;
            oob.length = clmlen;
            xioctl(fd, MEMWRITEOOB, & amp;oob);
        } else {<!-- -->
            xlseek(fd, erase.start, SEEK_SET);
            /* if (lseek(fd, erase.start, SEEK_SET) < 0) {
                bb_perror_msg("MTD %s failure", "seek");
                continue;
            } */
            xwrite(fd, & amp;cleanmarker, sizeof(cleanmarker));
            /* if (write(fd, & amp;cleanmarker, sizeof(cleanmarker)) != sizeof(cleanmarker)) {
                bb_perror_msg("MTD %s failure", "write");
                continue;
            } */
        }
        if (!(flags & amp; OPTION_Q))
            printf("Cleanmarker written at %x.", erase.start);
    }
    if (!(flags & amp; OPTION_Q)) {<!-- -->
        show_progress( & amp;meminfo, & amp;erase);
        bb_putchar('\\
');
    }

    if (ENABLE_FEATURE_CLEAN_UP)
        close(fd);
    return EXIT_SUCCESS;
}

Note:
Cleanmarker is used to mark whether a block has been completely erased. In Flash memory, writing operations can only be performed after the block has been completely erased. This is because in some special circumstances, such as the system restarting at the end of the erase cycle, the bits in the Flash may be unstable, that is, the values read out may be inconsistent at different points in time. To solve this problem, cleanmarker was introduced to mark whether a block is truly completely erased.

When making a jffs2 file system, since the file system manages blocks, there is no need to use the cleanmarker mark. When making a file system, you can use the -n or –no-cleanmarker option to indicate not using cleanmarker. This means that cleanmarker does not matter when the file system is first mounted. When using the mtd tool flash_eraseall to format Flash, you should use the -j parameter to bring the cleanmarker tag. In this way, after the Flash is erased, the corresponding cleanmark bit will be set in the oob (out-of-band) area, indicating that the block has been completely erased. In this way, when operating on the block in the future, the jffs2 file system can find that the block has been erased, thus avoiding wasting time re-erasing a block that is already 0xff.

flash_eraseall implementation flow chart