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节点定义。两种方式的共同点是:分区边界必须与擦除块大小、页大小、坏块管理策略对齐,否则会出现写入放大、擦除失败或启动镜像损坏。
二、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确认分区名与偏移一致。任何偏移不一致都可能导致启动失败或升级写入错误位置。