制作根文件系统
本项目默认生成适用于 NAND Flash 的 UBIFS 根文件系统。本文介绍 NAND Flash 和 NOR Flash 两种启动介质对应的根文件系统制作方法,包括分区配置、镜像打包和烧录文件准备。
适用平台:Hi3516CV608 / Hi3516CV610。
前置条件:已按默认配置完成 OpenHarmony(OHOS)编译。
不同启动介质使用的文件系统格式、打包工具和分区配置如下。介质相关约束统一列于第 7 节“注意事项”。
| 对比项 | NAND(SPI NAND) | NOR(SPI NOR) |
|---|---|---|
| 默认支持情况 | 默认编译产物可直接使用 | 需按本文调整配置并生成镜像 |
| 根文件系统 | UBIFS | JFFS2 |
| 打包工具 | mkubiimg.sh(内部调用 mkfs.ubifs 和 ubinize) |
fakeroot mkfs.jffs2 |
| 擦除块 | 128K | 64K |
| 默认分区布局(608 / 610) | 37M / 203M | 15M / ≈46M |
编译产物目录(下文记作 $OUT):
ohos/out/hispark_<chip>/ipcamera_hispark_<chip>_linux/
├── boot_image.bin # U-Boot
├── uImage # kernel + dtb
├── rootfs/ # 已完成 BusyBox 后处理的根目录,包含内核模块、动态库和初始化脚本
├── nand_env.bin # NAND 用
├── nand_burn_table.xml # NAND 用
├── rootfs_<chip>.ubifs # NAND 用(实际烧录的镜像)
└── rootfs_<chip>_64k.jffs2 # NOR 使用,需单独生成
1. NAND 根文件系统配置
NAND 编译产物可直接使用,通常无需调整;更换 NAND 芯片时,需补充芯片表项并重新编译。相关注意事项见第 7.1 节。
1.1 CV608
| 配置项 | 配置值 | 来源 |
|---|---|---|
| 分区布局 | 512K(boot),512K(env),4M(kernel),32M(rootfs) = 37M |
env_text/hi3516cv608/nand_env.txt |
| bootcmd | nand read 0x41000000 0x100000 0x400000; bootm 0x41000000 |
同上 |
| bootargs 关键段 | root=ubi0:ubifs rootfstype=ubifs rw ubi.mtd=3 mtdparts=nand:... |
同上 |
| rootfs 镜像 | rootfs_hi3516cv608.ubifs,实测 4.75MiB |
$OUT |
| 烧录表 | ohos/device/soc/hisilicon/hi3516cv608/burn/nand_burn_table.xml,rootfs 分区 Length=0x2000000(32M) |
静态文件 |
1.2 CV610
| 配置项 | 配置值 | 来源 |
|---|---|---|
| 分区布局 | 512K(boot),512K(env),4M(kernel),198M(rootfs) = 203M |
env_text/hi3516cv610/nand_env.txt |
| bootcmd | 与 CV608 相同 | 同上 |
| bootargs 关键段 | 与 CV608 相同,内存参数为 mem=56M(CV608 为 mem=32m) |
同上 |
| rootfs 镜像 | rootfs_hi3516cv610_2k_128k_198M.ubifs,实测 39MiB |
$OUT |
| 烧录表 | ohos/device/soc/hisilicon/hi3516cv610/burn/nand_burn_table.xml,rootfs 分区 Length=0xC600000(198M) |
静态文件 |
2. NOR 根文件系统配置
使用 NOR 启动时,需调整内核命令行配置(仅 CV608)、生成环境变量镜像和烧录表,并打包 JFFS2 根文件系统镜像。
2.1 容量评估
NOR 使用 JFFS2 根文件系统,其镜像大小小于 UBIFS 镜像,但 NOR 芯片容量也较小。实测结果如下:
| 平台 | JFFS2 镜像实测大小 | 最小容量需求(boot + env + kernel + rootfs) | 结论 |
|---|---|---|---|
| CV608 | 2.60MiB | 512K+512K+3.17M+2.60M ≈ 6.77M | 8MB 及以上可用 |
| CV610 | 32.42MiB | 512K+512K+3.66M+32.42M ≈ 37.1M | 32MB 容量不足 |
- CV608:使用 8MB 芯片时,按 4M kernel 分区配置,rootfs 分区可分配 3M,镜像约占该分区的 87%。此配置下 JFFS2 垃圾回收较为频繁,建议使用 16MB 及以上容量的芯片。
- CV610:完整 rootfs 镜像约为 32.42MiB,32MB NOR 芯片无法容纳。可根据产品需求裁剪未使用的内核模块和动态库,选用 64MB 及以上容量的 NOR 芯片,或使用 NAND。选用 64MB 及以上容量的 NOR 芯片时,需补充芯片表项并启用 4 字节地址模式。
2.2 CV608 NOR 配置步骤
步骤一:调整内核命令行配置
CV608 的内核 defconfig 启用了 CONFIG_CMDLINE_FORCE=y,且 CONFIG_CMDLINE 使用 NAND 启动参数。启动时,early_init_dt_scan_chosen() 中的 #elif defined(CONFIG_CMDLINE_FORCE) 分支(位于 drivers/of/fdt.c:1067)会使用该配置覆盖 U-Boot 传入的 bootargs。因此,仅将环境变量修改为 JFFS2 参数后,内核仍会尝试挂载 ubi0,导致 NOR 启动失败,需同步修改内核命令行配置。
将以下两个文件第 26 行的 CONFIG_CMDLINE 修改为与 env_text/hi3516cv608/nor_env.txt 中 bootargs 完全一致的值:
ohos/kernel/linux/config/linux-5.10/arch/arm/configs/.hispark_hi3516cv608_small_defconfig.orig # 第 26 行
ohos/kernel/linux/config/linux-5.10/arch/arm/configs/hispark_hi3516cv608_small_defconfig # 第 26 行
两个文件需同步修改。.orig 文件是 scripts/build_ohos.sh:475-481 使用的备份基线,overlay_case_defconfig() 会在每轮构建时据此还原 defconfig;仅修改后者会被覆盖。修改完成后,重新编译 uImage。
步骤二:根据芯片容量配置分区
修改 env_text/hi3516cv608/nor_env.txt。使用 8MB 芯片时,将 rootfs 分区配置为 3M,即 ...,4M(kernel),3M(rootfs);使用 16MB 及以上容量的芯片时,可保留以下 10M rootfs 分区配置:
bootargs=... rw root=/dev/mtdblock3 rootfstype=jffs2 mtdparts=sfc:512K(boot),512K(env),4M(kernel),10M(rootfs)
步骤三:生成镜像并准备烧录文件
按第 3 节生成环境变量镜像和烧录表,按第 4.1 节打包 JFFS2 镜像,再按第 5.1 节准备并检查烧录文件。
2.3 CV610 NOR 配置步骤
CV610 的内核配置为 CONFIG_CMDLINE="",且未启用 CONFIG_CMDLINE_FORCE,可直接使用 U-Boot 传入的 bootargs,无需执行第 2.2 节的内核命令行修改步骤。容量方面需使用 64MB 芯片,容量评估见第 2.1 节。
步骤一:根据芯片容量配置分区
修改 env_text/hi3516cv610/nor_env.txt。使用 64MB 芯片时,可将 rootfs 分区配置为 59M:
bootargs=... rw root=/dev/mtdblock3 rootfstype=jffs2 mtdparts=sfc:512K(boot),512K(env),4M(kernel),59M(rootfs)
步骤二:生成镜像并准备烧录文件
按第 3 节生成环境变量镜像和烧录表,按第 4.1 节打包 JFFS2 镜像,再按第 5.1 节准备并检查烧录文件。
3. 生成 NOR 环境变量镜像和烧录表
从工程根目录 hi_aiot_solution/ 执行以下命令:
CHIP=hi3516cv608 # 或 hi3516cv610
cd sdk/Hi3516CV610/boards/dmeb/tools/pc/uboot_env
make ENVTXT=$PWD/env_text/$CHIP/nor_env.txt
mv env.bin nor_env.bin && mv burn_table.xml nor_burn_table.xml
make 依次调用 mkenvimage -s 0x40000 -o env.bin <env.txt> 生成环境变量镜像,并调用 python3 scripts/env2burn.py <env.txt> burn_table.xml,根据 mtdparts=sfc:... 生成烧录表。调整分区时,修改 mtdparts 并重新执行 make 即可生成烧录表,无需手动编写 XML。生成后,检查烧录表引用的镜像文件名:
烧录表中的 SelectFile 应与实际镜像文件名一致,命名约束见第 7.2 节。
4. 打包根文件系统
两种介质均使用 $OUT/rootfs 作为打包源目录。该目录已完成 BusyBox 后处理,包含内核模块、动态库和初始化脚本。两种介质使用的源目录内容相同,打包工具和参数不同。
4.1 NOR:生成 JFFS2 镜像
cd ohos/out/hispark_<chip>/ipcamera_hispark_<chip>_linux/
fakeroot mkfs.jffs2 -U -d ./rootfs -l -e 0x10000 -o rootfs_<chip>_64k.jffs2
| 参数 | 含义 |
|---|---|
fakeroot |
使 -U 对文件属主和权限的设置生效;在编译主机上以普通用户直接执行时,可能记录错误的属主 |
mkfs.jffs2 |
由 mtd-utils 提供;可通过 apt install mtd-utils fakeroot 安装所需工具 |
-d ./rootfs |
源目录,即 $OUT/rootfs |
-l |
小端(ARM) |
-e 0x10000 |
擦除块大小为 64K,必须与芯片实际值一致,否则板端挂载失败;见第 7.2 节 |
-U |
将所有文件的属主和属组统一设置为 root:root |
-o rootfs_<chip>_64k.jffs2 |
输出文件名,应与烧录表中的引用一致 |
打包后,检查镜像大小和文件头。镜像大小应小于 rootfs 分区容量,容量评估见第 2.1 节。
4.2 NAND:生成 UBIFS 镜像
NAND 镜像制作分为两个阶段:先使用 mkfs.ubifs 生成文件系统镜像,再使用 ubinize 根据 NAND 物理参数生成可烧录的 UBI 镜像。SDK 通过 sdk/Hi3516CV610/boards/dmeb/tools/pc/ubi_sh/mkubiimg.sh 封装上述流程;OHOS 构建流程使用内容相同的 scripts/ohos/mkubiimg.sh。
cd ohos/out/hispark_<chip>/ipcamera_hispark_<chip>_linux/
./mkubiimg.sh <chip> 2k 128k rootfs <rootfs分区大小> pc
CV608 和 CV610 的命令示例如下:
# CV608:rootfs 分区 32M(对应 nand_env.txt 的 32M(rootfs))
./mkubiimg.sh hi3516cv608 2k 128k rootfs 32M pc
# CV610:rootfs 分区 198M(对应 nand_env.txt 的 198M(rootfs))
./mkubiimg.sh hi3516cv610 2k 128k rootfs 198M pc
| 位置 | 含义 |
|---|---|
mkubiimg.sh |
在 $OUT 目录下执行;若脚本不存在,可从 scripts/ohos/mkubiimg.sh 复制 |
第 1 个 <chip> |
芯片名称,用于生成输出文件名:rootfs_<chip>.ubifs |
第 2 个 2k |
NAND 页大小,当前两种 NAND 芯片均为 2K,以 -m 2048 传入 mkfs.ubifs |
第 3 个 128k |
NAND 块大小,以 -p 131072 传入 ubinize |
第 4 个 rootfs |
源目录($OUT/rootfs) |
第 5 个 32M/198M |
rootfs 分区大小,必须与 nand_env.txt 中 mtdparts 的 rootfs 分区大小一致 |
第 6 个 pc |
工具路径占位参数;通过 PATH 查找 mkfs.ubifs 和 ubinize 时,可传入任意值 |
调用脚本时需提供全部 6 个参数。以 32M rootfs 分区为例,脚本内部处理流程等价于:
# 1) 计算逻辑擦除块:LEB = 块大小 - 页大小 × 2 = 128K - 2×2K = 126976 (0x1F000)
# 2) 根据 LEB 和最大 LEB 数(分区大小 / 块大小 = 32M/128K = 256)生成 UBIFS 镜像
mkfs.ubifs -F -d rootfs -m 2048 -e 126976 -c 256 -o rootfs_<chip>_2k_128k_<size>.ubiimg
# 3) 生成 ubinize 配置:单卷、动态卷、autoresize
# [ubifs-volumn] mode=ubi / vol_type=dynamic / vol_name=ubifs / vol_flags=autoresize
# 4) 按物理参数打包成可烧写的 UBI 镜像
ubinize -o rootfs_<chip>.ubifs -m 2048 -p 131072 rootfs_<chip>_2k_128k_<size>.ubicfg
输出文件为 rootfs_<chip>.ubifs,中间文件 .ubiimg 和 .ubicfg 默认会被删除。打包后检查镜像大小和文件头:
ls -l rootfs_<chip>.ubifs # 应小于分区大小;自动扩容机制见第 7.1 节
xxd -l 4 rootfs_<chip>.ubifs # 前 4 字节应为 UBI#(0x55424923)
打包后,按第 7.1 节核对镜像文件名与烧录表中的 SelectFile。
5. 烧录文件清单
5.1 NOR
| 分区 | 文件 | 分区容量 | 来源 |
|---|---|---|---|
| boot | boot_image.bin |
512KB | 编译产物;未支持的芯片型号需补充表项后重新编译 |
| env | nor_env.bin |
512KB | 按第 3 节生成 |
| kernel | uImage |
4MB | 编译产物;CV608 需先完成第 2.2 节的内核命令行配置 |
| rootfs | rootfs_<chip>_64k.jffs2 |
见第 2.2、2.3 节 | 第 4.1 节打包 |
烧录表 nor_burn_table.xml 如下,默认分区总容量为 15M;小容量芯片以实际生成的烧录表为准。
<Partition_Info>
<Part Sel="1" PartitionName="boot" FlashType="spi" FileSystem="none" Start="0x0" Length="0x80000" SelectFile="boot_image.bin" />
<Part Sel="1" PartitionName="env" FlashType="spi" FileSystem="none" Start="0x80000" Length="0x80000" SelectFile="nor_env.bin" />
<Part Sel="1" PartitionName="kernel" FlashType="spi" FileSystem="none" Start="0x100000" Length="0x400000" SelectFile="uImage" />
<Part Sel="1" PartitionName="rootfs" FlashType="spi" FileSystem="none" Start="0x500000" Length="0xa00000" SelectFile="rootfs_hi3516cv608_64k.jffs2" />
</Partition_Info>
将环境变量镜像和烧录表复制到编译产物目录,并执行烧录配置检查:
cp ../../../../sdk/Hi3516CV610/boards/dmeb/tools/pc/uboot_env/{nor_env.bin,nor_burn_table.xml} .
bash tools/burn/check_config.sh <镜像目录> NOR
5.2 NAND
| 分区 | 文件 | 分区容量 | 来源 |
|---|---|---|---|
| boot | boot_image.bin |
512KB | 编译产物 |
| env | nand_env.bin |
512KB | 编译产物 |
| kernel | uImage |
4MB | 编译产物 |
| rootfs | 608:rootfs_hi3516cv608.ubifs610: rootfs_hi3516cv610_2k_128k_198M.ubifs |
608:32MB 610:198MB |
第 4.2 节打包 |
使用 ohos/device/soc/hisilicon/<chip>/burn/nand_burn_table.xml 作为烧录表(使用要求见第 7.1 节),并执行烧录配置检查:
6. 常见问题与处理方法
| 现象 | 介质 | 原因与处理 |
|---|---|---|
can't find in the ID table !!! + Failed to initialize SPI flash |
NOR | U-Boot NOR 芯片表未包含该型号,按第 7.2 节补充表项并重新编译 U-Boot |
内核 unrecognized JEDEC id bytes、/proc/mtd 为空 |
NOR | 内核 SPI NOR 芯片表未包含该 ID,按第 7.2 节补充表项 |
CV608 配置 NOR 环境变量后,内核仍尝试挂载 ubi0 |
NOR | CONFIG_CMDLINE_FORCE=y 覆盖了 bootargs,按第 2.2 节步骤一处理后重新编译 uImage |
| JFFS2 挂载失败或回读校验失败 | NOR | 常见原因为 -e 与芯片擦除块大小不一致,需核对 /proc/mtd,并同步调整镜像文件名与烧录表 |
| 烧录时提示镜像超出分区 | NOR | 镜像大于 rootfs 分区,按第 2.1 节重新评估容量,并按第 3 节重新生成相关文件 |
| 内核启动停滞或无法找到根文件系统 | NOR | rootfs 不再是第 4 个分区,或 bootcmd 中 kernel 的偏移、长度与烧录表不一致 |
| 大于 16MB 的芯片启动失败,U-Boot 报地址模式错误 | NOR | 表项 addrcycle 与 FMC 启动地址模式不匹配 |
| 板端权限异常或应用无法启动 | 两者 | JFFS2 打包时未使用 -U 及 fakeroot |
saveenv 报错或环境变量丢失 |
NOR | 芯片擦除块大于 CONFIG_ENV_SECT_SIZE(64K),需调整该配置并重新编译 U-Boot |
mkubiimg.sh 仅输出用法说明,未生成镜像 |
NAND | 参数数量不符合要求(需提供 6 个,见第 4.2 节);脚本以状态码 0 退出,需检查目标文件是否生成 |
UBIFS 挂载失败或无法找到 ubi0 |
NAND | 页大小、块大小与芯片不一致,或分区大小超出芯片容量;按第 4.2 节核对 |
烧录表 SelectFile 为空或指向不存在的文件 |
NAND | 使用 env2burn.py 重新生成了 NAND 烧录表,或镜像文件名与烧录表不一致;见第 7.1 节 |
7. 注意事项
7.1 NAND 注意事项
- 烧录表来源与文件名一致性:使用
ohos/device/soc/hisilicon/<chip>/burn/nand_burn_table.xml,不得通过env2burn.py重新生成 NAND 烧录表。该静态烧录表已与实际文件名保持一致,也可手动调整SelectFile。CV610 静态烧录表引用的文件名为rootfs_hi3516cv610_2k_128k_198M.ubifs,可能与手动打包生成的rootfs_<chip>.ubifs不同,需在打包后按烧录表统一文件名,否则烧录时无法找到文件。OHOS 构建脚本scripts/build_ohos.sh:673-681中的align_rootfs_image_name()用于完成此项处理。 - 镜像大小与分区容量:UBIFS 对应卷配置了
autoresize,首次挂载时会自动扩展至可用分区空间。因此,镜像大小明显小于分区容量属于正常情况。例如,CV608 的镜像约为 4.75MiB,而 rootfs 分区为 32M。不应仅依据镜像大小缩减分区。 - 页大小与块大小:打包参数必须与实际 NAND 芯片一致。当前两种 NAND 芯片均使用 2K 页和 128K 擦除块,对应第 4.2 节命令中的
2k 128k。
7.2 NOR 注意事项
- 擦除块大小:
mkfs.jffs2 -e必须与芯片实际擦除块大小一致。当前 SDK 未启用CONFIG_MTD_SPI_NOR_USE_4K_SECTORS,本文配置使用 64K 擦除块,对应-e 0x10000。可通过板端cat /proc/mtd核对erasesize列,其值应为00010000;U-Boot 的sf probe输出也会包含块大小信息,例如Block:64KB Chip:16MB Name:"..."。 - 芯片支持范围:新增芯片型号时,应核对 U-Boot 和内核中的芯片表项。U-Boot 表位于
drivers/mtd/spi/fmc100/fmc_spi_nor_ids.c,内核表位于drivers/mtd/spi-nor/gigadevice.c。适配修改可参考apps/cv610_ev_board/samples/fastboot/nor_fastboot/patch/uboot_flash_adapter.patch。 - 根文件系统分区序号:rootfs 必须为第 4 个分区。挂载参数固定为
root=/dev/mtdblock3,增加或删除分区会导致设备编号与挂载参数不匹配。 - kernel 分区与读取参数一致性:修改 kernel 分区长度后,必须同步修改
bootcmd。例如,sf read 0x41000000 0x100000 0x400000中第三个参数为 kernel 读取长度;扩大分区后未同步调整该参数,会导致uImage被截断。涉及 U-Boot 编译配置的修改应写入 defconfig 后重新编译,不能仅修改临时.config,因为u-boot/Makefile的all目标会先执行distclean并重新生成配置。 - MTD 设备名称:
mtdparts=sfc:的前缀来自设备树节点名称。不得修改hi3516cv6xx-demb.dts中&sfc { sfc@0 {...} }的节点名称,否则无法生成分区。 - 环境变量擦除范围:
CONFIG_ENV_SECT_SIZE=0x10000表示 SPI 环境变量使用 64K 擦除范围。芯片要求的最小擦除块更大时,需相应调整此配置并重新编译 U-Boot。 - 镜像文件命名:烧录表生成脚本使用固定的 NOR 文件名
rootfs_<chip>_64k.jffs2,不会根据mkfs.jffs2 -e参数调整文件名。使用非 64K 擦除块时,应同步调整镜像文件名或烧录表中的SelectFile,确保引用一致。