OpenHarmony 镜像交付避坑:从 FIT、NAND 分区到 rootfs 版本指纹
1. 为什么“编译成功”仍可能烧出错误镜像
OpenHarmony 设备开发中,经常出现一种看似矛盾的现象:源码已经修改,完整构建也输出 build success,烧录后板端却仍表现为旧问题。
原因通常不在编译器,而在交付链路:
源码
-> GN/Ninja 中间产物
-> kernel / rootfs / FIT
-> 产品 OUT
-> Windows 烧录目录
-> 开发板运行版本
其中任何一层没有刷新,都可能把旧文件带入最终板卡。本文总结一套适用于 OpenHarmony 小型系统的镜像一致性检查方法。
2. 正确 DTS 不等于正确 FIT
项目中同时存在基础 DTB 和产品 DTB 时,最典型的问题是:产品 DTS 已经修好,但 kernel.mk 仍把基础 DTB 打进 uImage。
只检查源码或构建目录中的 .dtb 不够,必须从最终 FIT 反向提取:
dumpimage -T flat_dt -p 0 -o embedded.dtb uImage
dtc -I dtb -O dts -o embedded.dts embedded.dtb
然后执行两类检查。
第一类是字节一致性:
cmp -- embedded.dtb expected-product.dtb
第二类是语义检查。例如某个 NAND 产品要求 eMMC、物理 SDIO0 和冲突显示外设关闭,物理 SDIO1 开启并带 non-removable,就应逐节点断言,而不是只判断文件名叫不叫 flash.dtb。
还要注意 FIT 中镜像索引。某些产品的 Image 0 是设备树,Image 1 才是内核;索引用错会把正确镜像误判为失败。
3. NAND 分区、U-Boot 环境和烧录表必须一致
调整 kernel 分区时,不能只修改 nand_burn_table.xml。至少要同时核对:
- 烧录表中的起始地址和长度;
- U-Boot
mtdparts; bootcmd的 NAND 读取地址与长度;- rootfs 分区起点;
- 擦除块对齐;
- 文件对齐后的实际写入长度。
某次实践中,FIT 超过 4 MiB 后,烧录工具虽然显示写入成功,但板端回读发现 4 MiB 之后的尾部损坏,U-Boot 随后报 FIT 格式错误。最终通过 XZ 压缩把内核控制在边界以内,同时恢复运行时真正使用的 4 MiB kernel 布局,才让 kernel 与 rootfs 地址重新一致。
这说明“分区足够大”并不等于“烧录实现可以可靠完成单次大块写入”。对大镜像最好同时做整文件和分块校验。
4. rootfs 可能被时间戳和增量图卡住
另一个常见问题是源文件内容已更新,OUT 或 rootfs 仍保留旧版本。
如果 overlay 使用 cp -a,源文件的旧时间戳会被保留。旧 OUT 反而比正确源文件更新时,Ninja 可能判断复制动作无需执行。此时重新跑增量构建也不会刷新文件。
更稳妥的方式是:
cp -a --no-preserve=timestamps overlay/. "$PROJECT_ROOT/"
或者只清理已经确认失效的具体 stamp/object,再重建目标。不要为了刷新一个配置就删除整个 OUT,更不要用泛化的 git clean 处理包含用户改动的工作区。
对于关键运行文件,建议做三方比较:
cmp source/init.cfg "$OUT/etc/init.cfg"
cmp source/init.cfg "$OUT/rootfs/etc/init.cfg"
5. 不要用历史 error.log 否定当前成功构建
部分 OpenHarmony 构建目录在后续成功后仍会保留上一次失败的 error.log。因此,判断当前轮次是否成功,应以以下证据组合为准:
- 本次命令退出码;
- 独立时间戳日志中的
build success; - 本轮目标是否实际重新编译/链接;
- 产物时间、大小和哈希;
- rootfs/UBIFS 是否重新生成。
旧错误日志应保留用于追踪,但不能把“文件非空”直接解释为当前构建失败。
6. OUT 与烧录目录必须作为一个交付事务
开发者经常在 Linux/WSL 中生成新 OUT,却让烧录工具继续使用 Windows 上一次选择的旧目录。最安全的做法是:每次 OUT 变化,都在同一轮同步完整五件套并逐项比较。
示意检查:
set -euo pipefail
for f in nand_burn_table.xml boot_image.bin nand_env.bin uImage rootfs_hi3516cv610.ubifs; do
cmp -- "$OUT/$f" "$BURN_DIR/$f"
sha256sum "$OUT/$f" "$BURN_DIR/$f"
done
同时解析烧录表,确认其中引用的文件都在同一目录存在,分区长度大于文件大小。只同步 uImage 或 rootfs,不能证明 boot/env/XML 与之匹配。
7. 烧录后先做版本指纹,再做功能测试
板端出现问题时,第一件事不是改代码,而是确认运行的确实是本轮镜像。
建议记录以下指纹:
uname -a
关键服务程序大小与校验和
关键共享库校验和
三个 KO 的大小、vermagic 和校验和
最终配置文件内容
设备树关键节点
如果板端缺少 sha256sum,可以使用主机和板端都支持的 md5sum、cksum 加文件长度完成比对。版本不一致时,任何功能结果都只能算旧镜像证据,不能用于评价本轮修改。
8. 把验证分成五个独立状态
建议明确区分:
- 源码和配置已修改;
- 产品完整构建成功;
- 最终镜像静态检查通过;
- 烧录成功且板端版本指纹匹配;
- 冷启动、热重启和业务功能实测通过。
上一个状态不能自动推导下一个状态。比如 FIT 可以正常解析,不代表驱动已加载;板端文件哈希一致,也不代表 Wi-Fi 功能已经通过;扫描成功,也不能外推 DHCP 和断开流程成功。
9. 发布前检查清单
最后给出一份简化门禁:
- 本次完整构建退出码为 0;
- 最终 FIT 内嵌 DTB 与产品目标一致;
- 关键设备树节点语义正确;
- kernel、rootfs、XML、U-Boot 环境布局一致;
- rootfs 中服务、库、KO、固件和配置来自本轮;
- OUT 与烧录目录五件套逐字节一致;
- 烧录后板端版本指纹匹配;
- 冷启动和热重启分别验证;
- 业务测试只声明实际执行过的范围。
10. 总结
镜像交付的本质不是“把几个文件复制过去”,而是建立从源码到板端运行状态的可追踪关系。最终 FIT、rootfs、分区布局、烧录目录和板端指纹缺一不可。
当每一层都有可重复的检查命令后,很多“同样的代码为什么这次不工作”的问题,都会变成可以迅速定位的版本或产物一致性问题。
更多推荐
所有评论(0)