Skip to content

Flash Storage trong Linux kernel: MTD và MMC

Trang này mô tả cách Linux kernel tổ chức code và giao diện cho bộ nhớ flash, gồm các subsystem liên quan, cách khai báo trong device tree, các node trong /dev và cách mount rootfs. Phần cứng (cell, NOR/NAND, ECC, eMMC) được trình bày tại Flash: NOR, NAND và eMMC; so sánh và cách chọn loại bộ nhớ cho board xem Chọn bộ nhớ non-volatile cho hệ nhúng.

Tổng quan: MTD và MMC

Storage stack trong Linux: MTD và Block

Raw flash (NOR, raw NAND, SPI NAND) Managed NAND (eMMC, SD card)
Subsystem MTD MMC + block layer
Ai lo ECC, bad block, wear leveling Kernel (driver NAND, UBI) Controller trong chip
Node chính /dev/mtdN, /dev/ubiX_Y /dev/mmcblkN, /dev/mmcblkNpM
Filesystem UBIFS, JFFS2, squashfs ext4, f2fs

MTD subsystem

MTD (Memory Technology Device) là lớp trừu tượng cho flash thô, cung cấp ba thao tác đọc, ghi và xóa theo erase block. Code nằm trong drivers/mtd/:

Thư mục Nội dung
drivers/mtd/ MTD core, phân vùng, char device mtdchar, mtdblock
drivers/mtd/spi-nor/ SPI NOR core; controller QSPI/OSPI cung cấp thao tác qua spi-mem (xem SPI Subsystem)
drivers/mtd/nand/raw/ Raw NAND core và driver NAND controller của từng SoC
drivers/mtd/nand/spi/ SPI NAND
drivers/mtd/ubi/ UBI

Node trong /dev:

  • /dev/mtdN: char device, dùng cho các công cụ đọc, ghi, xóa (flash_erase, nandwrite). Có thêm /dev/mtdNro chỉ đọc.
  • /dev/mtdblockN: block device chỉ nên dùng để đọc hoặc mount filesystem chỉ đọc (squashfs). Không có wear leveling, nên không dùng để ghi thường xuyên.

Phân vùng MTD

Phân vùng thường được khai báo trong device tree, bên trong node của chip flash:

&qspi {
    flash@0 {
        compatible = "jedec,spi-nor";
        reg = <0>;
        spi-max-frequency = <50000000>;

        partitions {
            compatible = "fixed-partitions";
            #address-cells = <1>;
            #size-cells = <1>;

            partition@0 {
                label = "boot";
                reg = <0x000000 0x100000>;
                read-only;
            };
            partition@100000 {
                label = "env";
                reg = <0x100000 0x020000>;
            };
            partition@120000 {
                label = "rootfs";
                reg = <0x120000 0xee0000>;
            };
        };
    };
};

Cũng có thể truyền phân vùng qua kernel command line, ví dụ mtdparts=spi0.0:1M(boot)ro,128k(env),-(rootfs). Cách này thuận tiện khi bootloader (U-Boot) đã quản lý bảng phân vùng.

UBI và UBIFS

Trên raw NAND, ghi trực tiếp lên MTD không có wear leveling và bad block management. UBI (Unsorted Block Images) chạy trên một phân vùng MTD để đảm nhận các chức năng này, và chia phân vùng thành các volume (/dev/ubiX_Y). UBIFS là filesystem chạy trên một UBI volume.

/dev/mtd2  ──►  UBI device ubi0  ──►  volume ubi0:rootfs (/dev/ubi0_0)  ──►  UBIFS
ubiformat /dev/mtd2                      # chuẩn bị phân vùng cho UBI, giữ lại erase counter
ubiattach /dev/ubi_ctrl -m 2             # gắn mtd2 thành ubi0
ubimkvol /dev/ubi0 -N rootfs -m          # tạo volume "rootfs" chiếm toàn bộ dung lượng còn lại
mount -t ubifs ubi0:rootfs /mnt

Với phân vùng đã dùng UBI, nên dùng ubiformat thay cho flash_erase để không mất erase counter mà UBI dùng cho wear leveling.

MMC subsystem

MMC subsystem điều khiển eMMC, SD card và SDIO. Code nằm trong drivers/mmc/:

Thư mục Nội dung
drivers/mmc/core/ Khởi tạo thẻ, quản lý bus, driver block (block.c)
drivers/mmc/host/ Driver host controller: sdhci-* (SDHCI chuẩn), dw_mmc* (Synopsys)...

Khai báo host controller trong device tree, ví dụ một eMMC 8-bit:

&sdhci0 {
    bus-width = <8>;
    non-removable;
    mmc-hs200-1_8v;
    status = "okay";
};

Node trong /dev:

  • /dev/mmcblkN: toàn bộ vùng dữ liệu (user area); /dev/mmcblkNpM là các phân vùng trong bảng MBR/GPT.
  • /dev/mmcblkNboot0, boot1: boot partition của eMMC, mặc định chỉ đọc. Cho phép ghi bằng lệnh echo 0 > /sys/block/mmcblk0boot0/force_ro.
  • /dev/mmcblkNrpmb: vùng RPMB, truy cập qua mmc-utils hoặc TEE.

Mount rootfs từ flash

Rootfs được chỉ định qua kernel command line (xem Kernel Command Line):

Rootfs nằm trên Tham số
eMMC, phân vùng 2 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait
UBIFS trên phân vùng MTD tên rootfs ubi.mtd=rootfs root=ubi0:rootfs rootfstype=ubifs
squashfs trên NOR, phân vùng MTD số 3 root=/dev/mtdblock3 rootfstype=squashfs ro

rootwait là cần thiết với eMMC và SD card, do thiết bị có thể được phát hiện muộn hơn thời điểm kernel tìm rootfs.

Kiểm tra và debug

cat /proc/mtd                            # danh sách phân vùng MTD, kích thước, erase size
mtdinfo -a                               # chi tiết từng MTD device (mtd-utils)
cat /sys/class/mtd/mtd0/{type,erasesize,writesize,oobsize}
ubinfo -a                                # UBI device và volume
lsblk /dev/mmcblk0                       # eMMC và các phân vùng
cat /sys/block/mmcblk0/device/{name,type}
cat /sys/kernel/debug/mmc0/ios           # clock, bus width, timing đang dùng (debugfs)
mmc extcsd read /dev/mmcblk0             # thanh ghi EXT_CSD (mmc-utils)
dmesg | grep -i -E "mtd|ubi|mmc"