MTD 与 Flash

Linux 工作站和服务器一般使用磁盘作为存储设备,而嵌入式 Linux 倾向使用 Flash 固态存储设备。
相比磁盘的“读”和“写”操作,Flash 还需要“擦除”,并且它还有写寿命(常见为 10 万次)。
为了更好地操作 Flash 设备 Linux 添加了 MTD 子系统(Memory Technology Device)。

MTD@Flash 和 磁盘块设备 的区别如下表所示

操作单位

操作

耗损均衡

MTD

擦除块

读/写/擦除

均匀分布写块

块设备

扇区

读/写

/

小窍门1
一般 MTD 设备指 Flash 芯片,常见的 SD/MMC CompactFlash USB闪存盘以及其他一些类似的设备更像传统的块设备(如磁盘)。

小窍门2
一般 Flash 分为 2 种类型 NOR 和 NAND,它们的区别如下表所示。

类别

容量

寻址单位

适合文件系统

管理坏块

性能

NOR


字节

JFFS2

/

写速度快

NAND



YAFFS2

记录坏块

读速度快

内核支持 MTD

内核选项

MTD 功能

CONFIG_MTD

支持 MTD 子系统

CONFIG_MTD_CONCAT

将多个 MTD 设备或分区组合成单一文件系统

CONFIG_MTD_PARTITIONS

支持 MTD 分区

CONFIG_MTD_CHAR

通过 /dev/mtdN 和 /dev/mtdrN 操作 MTD 字符设备

CONFIG_MTD_BLOCK

通过 /dev/mtdblockN 操作 MTD 块设备

CONFIG_MTD_CMDLINE_PARTS

在命令行中传递分区信息

操作 MTD 分区

  • 查看 MTD 分区 cat /proc/mtd
  • 擦除 MTD 分区 flash_eraseall /dev/mtdX
  • 写 MTD 分区 NOR Flash flashcp /tmp/mtd.bin /dev/mtdX
  • 写 MTD 分区 NAND Flash nandwrite /tmp/image.bin /dev/mtdX
  • 读 MTD 分区 dd if=/dev/mtdX of=/tmp/mtd.bin

MTD 与文件系统

为了克服 Flash 自身的 2 个限制:擦除块耗时易导致断电丢失数据,写块有寿命限制。
支持它的文件系统一般需要具备:断电可靠性 和 耗损平衡(wear leveling 尽可能将数据均匀散布在 Flash 上)
常见的 MTD 文件系统包括:JFFS2 UBIFS YAFFS2 详情请链接Linux 文件系统类型

MTD 分区

和磁盘不同,不能使用 fdisk 之类的工具给 MTD 分区。

一般而言 MTD 分区有如下 3 种办法

  • 在内核命令行中定义分区表
  • 使用与具体板卡相关的映射驱动
  • 解析 Redboot TI_AR7 分区表

在 u-boot 引导的系统中使用方法 1 最简单;方法 2 需要修改 kernel 源代码并编译;方法 3 依赖特定的引导程序。

下面基于方法 1 给 MTD 分区
有一 128M NAND Flash 需要分成 5 个区,如下表所示

区块

分区 1

分区 2

分区 3

分区 4

分区 5

名称

u-boot

kernel

rootfs

config

log

大小

2M

20M

40M

2M

64M

按上面分区信息定义如下 u-boot 的参数,在启动时传递给内核,它会在 MTD 子系统中自动分区。

setenv bootargs 'noinitrd root=/dev/mtdblock2 rootfstype=yaffs2 rootflags=inband-tags console=ttyS0 rdinit=/sbin/init mem=64M mtdparts=nand0:2M(u-boot),20M(kernel),40M(rootfs),2M(config),-(log) ignore_loglevel'

启动 Linux 系统后,执行命令 cat /proc/mtd 即可查看 MTD 分区。

dev:    size   erasesize  name
mtd0: 00200000 00020000 "u-boot"
mtd1: 01400000 00020000 "kernel"
mtd2: 02800000 00020000 "rootfs"
mtd3: 00200000 00020000 "config"
mtd4: 04000000 00020000 "log"

编译与部署 MTD 工具

背景

为了升级 Linux 镜像,必然需要擦除/烧写 Flash,这是由 MTD 工具实现,它依赖 lzo libuuid 库。

编译 lzo 库

cd ${PRJROOT}/build-tools/lzo-2.09
./configure CC=${CROSS}-gcc --host=$CROSS --enable-shared
make
make prefix=$TARGET_PREFIX install

编译 libuuid 库

cd ${PRJROOT}/build-tools/libuuid-1.0.3
./configure --host=$CROSS
make
make prefix=$TARGET_PREFIX install

编译 MTD 工具

cd ${PRJROOT}/build-tools/mtd-utils
make CROSS="${CROSS}-" CFLAGS="-I ${TARGET_PREFIX}/include" LDFLAGS="-L ${TARGET_PREFIX}/lib"

部署 MTD 工具

cd $CROSS
MTD_TOOLS="./mtd_debug ./nandtest ./flash_erase ./nandwrite"
${CROSS}-strip $MTD_TOOLS
cp -vf $MTD_TOOLS <target_rootfs_dir>/usr/sbin/

大部分项目只需要上面 4 个 MTD 工具即可。幸运的是,它们只依赖 2 个库文件:libc(C 程序库) 和 ld(动态链接器)。
这 2 个库文件是基础软件(没有它们 Linux 几乎无法运行)。因此,不需要复制库文件到目标系统。