4. Flash分区与存储规划

在STM32MP的OTA与A/B升级体系中,分区规划是决定升级能否安全回滚、能否断电恢复、能否批量维护的底层基础。分区表一旦烧录到存储介质,后期调整往往意味着重新量产,因此必须在设计阶段就把存储介质特性、启动链路、文件系统布局和冗余策略一起考虑清楚。

一、先理解STM32MP的启动链路与分区依赖

STM32MP1系列(如STM32MP157)的启动过程通常分为ROM Code、FSBL、SSBL和内核几个阶段。ROM Code从所选启动介质读取FSBL,FSBL初始化DDR并加载SSBL(通常是U-Boot),SSBL再加载内核、设备树和根文件系统。这个链路决定了分区表必须满足两个硬性条件:一是启动阶段能按固定偏移或GPT分区名找到引导镜像;二是升级时能对非当前运行分区进行写入而不破坏正在使用的系统。

在eMMC和SD卡上,STM32MP通常使用GPT分区表,配合U-Boot的mmc命令和part命令按分区号或分区名访问。在NAND和NOR上,则更多依赖MTD分区,通过mtdparts或设备树中的partitions节点定义。两种方式的共同点是:分区边界必须与擦除块大小、页大小、坏块管理策略对齐,否则会出现写入放大、擦除失败或启动镜像损坏。

STM32MP eMMC/SD 典型 GPT 分区布局与 A/B 冗余关系 eMMC / SD 物理存储(GPT) GPT头 fsbl1 fsbl2 ssbl bootfs A bootfs B rootfs A GPT备份 fsbl1备份 fsbl2备份 ssbl备份 bootfs A' bootfs B' rootfs B A/B 槽位切换逻辑 当前运行:A 槽 升级写入:B 槽 校验通过 → 切换启动标志 → 重启进入 B 槽 冗余与恢复策略 fsbl/ssbl 双备份 bootfs 双槽 rootfs 双槽 + vendorfs 独立只读 NAND / NOR 的 MTD 分区与坏块处理差异 NAND(大容量、有坏块) 分区按擦除块对齐,需 UBI/UBIFS 坏块表 + 冗余副本,启动镜像多份 NOR(小容量、无坏块) 分区按扇区对齐,常用 JFFS2/UBIFS 适合存放 FSBL/SSBL 与环境变量

二、eMMC与SD卡:GPT分区表设计要点

eMMC和SD卡容量大、随机读写性能好,适合承载完整的Linux系统。STM32MP官方参考设计中,典型GPT分区顺序如下:

  • fsbl1 / fsbl2:存放TF-A的FSBL镜像,两份互为备份,偏移通常从0x00000000之后开始,大小约256KB~512KB。
  • ssbl:存放U-Boot及SPL,大小约1MB~2MB,同样建议双份。
  • bootfs A / bootfs B:存放内核镜像、设备树、initramfs,每个约32MB~64MB,使用FAT或ext4。
  • rootfs A / rootfs B:存放根文件系统,每个约512MB~2GB,使用ext4或UBIFS。
  • vendorfs:存放厂商定制数据、校准参数、序列号,独立只读或读写分区,约16MB~64MB。

分区大小不是拍脑袋决定的。以rootfs为例,若系统镜像解压后约380MB,考虑日志、临时文件和后续OTA增量,建议分配至少768MB。bootfs中内核约8MB、设备树约100KB、initramfs约12MB,加上余量,32MB是较稳妥的起点。GPT分区表本身占用约34个扇区(17KB),首尾各一份,设计时不要忽略。

三、bootfs、rootfs、vendorfs的职责划分

bootfs的核心是“可被U-Boot直接读取”。它通常格式化为FAT32或ext4,U-Boot通过load mmc 0:2 ${kernel_addr_r} Image这类命令加载。bootfs中应包含:内核镜像Image、设备树stm32mp157c-xxx.dtb、可选的initramfs.cpio.gz,以及一个记录当前槽位和升级状态的boot.conf或U-Boot环境变量文件。

rootfs是完整的Linux根文件系统,包含/bin、/lib、/etc、/usr等。A/B双槽设计下,两个rootfs分区内容一致但独立,升级时写入非活动槽,校验通过后切换。rootfs中不应存放需要跨升级保留的数据,这些数据应放在vendorfs或独立data分区。

vendorfs用于存放设备唯一性数据和厂商定制内容,例如MAC地址、校准参数、产品型号、加密密钥。它通常挂载为/vendor,升级时不覆盖,保证设备身份不丢失。若使用只读挂载,可进一步降低误写风险。

四、NAND与NOR的MTD分区差异

NAND和NOR使用MTD子系统,分区通过设备树中的partitions节点或内核命令行mtdparts定义。NAND存在坏块,分区边界必须按擦除块大小对齐,通常为128KB或256KB。NAND上的文件系统推荐UBIFS,它自带磨损均衡和坏块管理;启动镜像则需要多份冗余,例如fsbl放在多个擦除块中,由ROM Code按顺序尝试。

NOR容量小但可靠性高,适合存放FSBL、SSBL和环境变量。NOR的分区按扇区对齐,常见扇区大小为64KB或128KB。由于NOR无坏块,文件系统可选JFFS2或UBIFS,但JFFS2挂载时扫描整个分区,大容量下启动较慢,因此小容量NOR更常用。

五、冗余策略与升级状态标记

冗余策略的核心是“任何单点写入失败都不影响启动”。具体做法包括:

  • FSBL和SSBL各存两份,分别位于不同擦除块或不同分区,ROM Code和U-Boot按优先级尝试。
  • bootfs和rootfs采用A/B双槽,升级只写非活动槽,校验通过后更新启动标志。
  • vendorfs独立于A/B之外,升级不触碰,保证设备身份和校准数据不丢失。
  • 使用U-Boot环境变量bootslot、upgrade_available、bootcount记录状态,配合altbootcmd实现失败回滚。

一个可参考的U-Boot环境变量片段如下:

bootslot=A
bootcount=0
bootlimit=3
upgrade_available=0
altbootcmd=run bootcmd_B
bootcmd_A=setenv bootslot A; run load_kernel; bootm ${kernel_addr_r}
bootcmd_B=setenv bootslot B; run load_kernel; bootm ${kernel_addr_r}

当bootcount超过bootlimit时,U-Boot自动执行altbootcmd切换到备用槽,实现无人工干预的回滚。这个机制要求bootfs和rootfs的A/B槽都完整可用,因此分区规划时必须为两个槽预留同等空间。

六、分区规划检查清单

检查项 eMMC/SD NAND NOR
分区表类型 GPT MTD partitions MTD partitions
对齐单位 1MB 或 4MB 擦除块 128KB/256KB 扇区 64KB/128KB
FSBL/SSBL冗余 双分区 多擦除块副本 双分区
rootfs文件系统 ext4 UBIFS UBIFS/JFFS2
vendorfs 独立分区 独立MTD分区 独立MTD分区
升级状态记录 U-Boot env + boot.conf U-Boot env U-Boot env
分区规划完成后,建议用fdisk -l或parted -l核对GPT分区边界,用cat /proc/mtd核对MTD分区,并在U-Boot中执行part list mmc 0确认分区名与偏移一致。任何偏移不一致都可能导致启动失败或升级写入错误位置。
不要将rootfs和vendorfs放在同一个可写分区中。OTA升级rootfs时若误格式化整个分区,vendorfs中的数据会一并丢失,导致设备MAC地址、校准参数不可恢复。A/B双槽和vendorfs独立是两条必须同时满足的底线。