imx6ull 开发板,SD卡启动,给EMMC分区,烧系统。

imx6ull 开发板,SD卡启动,给EMMC分区,烧系统。

接上一篇博客,本文内容,采用脚本,一键分区格式化 ,一键烧录系统。

运行脚本需要 sfdisk 工具,上一篇博客有用 buildroot 安装 sfdisk 的方法。

emmc 重新规划分区:

分区

类型

起始扇区

结束扇区

扇区数

大小

文件系统

用途

p1 (主)

c

2048

133119

131072

64 MiB

FAT32

bootA(内核+设备树)

p2 (主)

c

133120

264191

131072

64 MiB

FAT32

bootB(内核+设备树)

p3 (主)

83

264192

4458495

4194304

2 GiB

ext4

rootfsA(当前运行系统)

p4 (扩展)

5

4458496

15269887

10811392

~5.16 GiB

扩展容器

p5 (逻辑)

83

4460544

8654847

4194304

2 GiB

ext4

rootfsB(待升级槽)

p6 (逻辑)

83

8656896

15269887

6612991

~3.15 GiB

ext4

data(用户数据 + recovery 子目录)

U-Boot 冗余 由 boot0 + boot1 分区,硬件双备份保证 ,把主分区槽位空出来,留给更关键的系统组件。

recovery 独立分区的需求不变,但受主分区数量限制,放入扩展分区会降低安全性。这里我们先保留 154M 空间不分配,待以后升级 U-Boot 支持 GPT 或改用 4 主分区方案时再调整。当前我们可将 recovery 的内容放在 data 分区的一个子目录(如/data/recovery)中,系统挂死时通过 U-Boot 命令行手动加载。

在 MBR 的扩展分区中,每一个逻辑分区前面都需要一个 EBR(扩展引导记录)来描述自己。

p5 EBR:4460544,p5数据起始扇区:4460544+2048 = 4462592

p6 EBR:8654848,p6数据起始扇区:8654848+2048= 8656896

分区脚本 emmc_part.sh 代码:

#!/bin/sh # 等待设备节点就绪 while [ ! -e $1 ] do sleep 1 echo "[Log]: wait for $1 appear" done DEV=$1 # 擦除旧的 MBR 分区表 (擦除前1MB) echo "[Log]: Wiping old partition table..." dd if=/dev/zero of=${DEV} bs=1024 count=1 sync # p1主分区 结束:1M + 64M = 65M # p2主分区 结束:65M + 64M = 129M # p3主分区 (rootfsA, 2G) 结束:129M + 2G = 2177M # p4扩展容器 起始:2177M # p5逻辑分区 (rootfsB, 2G) 结束:(1M(EBR) + 2177M) + 2G = 4226M # P6逻辑分区 data用户数据起始:1M(EBR) + 4226M = 4227M echo "[Log]: Creating partitions..." sfdisk --force ${DEV} << EOF 1M,64M,0c 65M,64M,0c 129M,2G,83 2177M,,0x05 2178M,2G,83 4227M,,83 EOF # 确保内核识别到新分区 再格式化分区 echo "[Log]: Reloading partition table..." partprobe ${DEV} 2>/dev/null || sleep 2 echo "[Log]: Formatting..." mkfs.fat ${DEV}p1 mkfs.fat ${DEV}p2 mkfs.ext4 -F ${DEV}p3 mkfs.ext4 -F ${DEV}p5 mkfs.ext4 -F ${DEV}p6 echo "[Log]: Partition and Format Complete!" sfdisk -l ${DEV}

SD卡 启动linux,运行脚本

分区、格式化 成功。

挂载测试(测试完后,卸载所有挂载点):
挂载测试是为了验证文件系统没有损坏,能正常读写。

mkdir -p /mnt/bootA /mnt/bootB /mnt/rootA /mnt/rootB /mnt/data

参考图片 挂载每个分区, p4 不要挂载。

卸载所有挂载点:

umount /mnt/bootA

umount /mnt/bootB

umount /mnt/rootA

umount /mnt/rootB

umount /mnt/data

验证已卸载,确认不再有 /mnt/bootA 等条目。

df -h

下一步,给 emmc 烧录系统:

我是nfs挂载的根文件系统 ,通过 NFS 传到板子上烧录。

在虚拟机上,把 u-boot.imx 内核、设备树、根文件系统压缩包 拷贝到 NFS 上自己创建的目录里。

1.烧录 U-Boot到 eMMC boot0/boot1 ,执行脚本 01_burn_uboot.sh

下面是脚本代码

#!/bin/sh # 01_burn_uboot.sh - 烧录 U-Boot 到 eMMC boot0/boot1 # 更新频率极低(仅 U-Boot 升级时使用) DEV=/dev/mmcblk1 echo "[Log] Burn U-Boot to eMMC" # 检查文件 if [ ! -f "u-boot.imx" ]; then echo "[Log] ERROR: u-boot.imx not found!" exit 1 fi echo "[Log] Burning to boot0..." echo 0 > /sys/block/mmcblk1boot0/force_ro dd if=u-boot.imx of=${DEV}boot0 bs=512 seek=2 echo 1 > /sys/block/mmcblk1boot0/force_ro sync echo "[Log] Burning to boot1 (backup)..." echo 0 > /sys/block/mmcblk1boot1/force_ro dd if=u-boot.imx of=${DEV}boot1 bs=512 seek=2 echo 1 > /sys/block/mmcblk1boot1/force_ro sync # 设置 u-boot 从boot0 启动 echo "[Log] Setting eMMC to boot from boot0..." mmc bootpart enable 1 1 ${DEV} # 确认 mmc extcsd read ${DEV} | grep PARTITION_CONFIG echo "[Log] U-Boot burn complete!"

开机测试:

开发板的拨码开关,选择 emmc 启动,开机

boot0 + boot1 ,双 U-Boot 备份完全正常。

boot0 和 boot1 各有一套独立的 U-Boot,双备份,但它们读写环境变量时, 共用一份 env。
env的存储位置,是由编译 u-boot 时指定偏移量。

固化环境变量:

从 boot0 启动:mmc partconf 1 1 1 1
从 boot1 启动: mmc partconf 1 1 2 1

切回 boot0 ,输入下面的命令,固化环境变量

mmc partconf 1 1 1 1
mmc dev 1 0

设置:启动后 使用 emmc 分区3 的根文件系统
可以读写
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p3 rootwait rw'

或者 只读挂载,防篡改,二选一。
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p3 rootwait ro'

设置:从 emmc 分区1,加载 内核 设备树
setenv bootcmd 'fatload mmc 1:1 0x80800000 zImage; fatload mmc 1:1 0x83000000 imx6ull-emmc.dtb; bootz 0x80800000 - 0x83000000'
保存 saveenv
重启 reset

2 . 烧录内核 + 设备树到 bootA 和 bootB

SD 卡 启动 linux,继续烧录。

下面是 脚本 02_burn_kernel.sh 代码

#!/bin/sh # 烧录内核 + 设备树到 bootA 和 bootB, 可以自己指定目标 # 内核/设备树 更新时使用 DEV=/dev/mmcblk1 echo "[Log] Burn Kernel / DTB to Boot Partitions" # 检查文件 if [ ! -f "zImage" ]; then echo "[Log] ERROR: zImage not found!" exit 1 fi DTB=$(ls *.dtb 2>/dev/null | head -1) if [ -z "$DTB" ]; then echo "[Log] ERROR: dtb file not found!" exit 1 fi echo "[Log] Copying to bootA (p1)..." mount ${DEV}p1 /mnt cp zImage /mnt/ cp $DTB /mnt/imx6ull-emmc.dtb sync umount /mnt echo "[Log] Copying to bootB (p2)..." mount ${DEV}p2 /mnt cp zImage /mnt/ cp $DTB /mnt/imx6ull-emmc.dtb sync umount /mnt echo "[Log] Kernel / DTB burn complete!"

运行脚本,然后验证:
mount /dev/mmcblk1p1 /mnt
ls /mnt
umount /mnt

用同样的方法,验证 mmcblk1p2

02_burn_kernel.sh 脚本的24、31行,拷贝时 DTB 改名字了,知道就行。

3 . 烧录 根文件系统

下面是 脚本 03_burn_rootfs.sh 代码

#!/bin/sh # 烧录根文件系统到 rootfsA DEV=/dev/mmcblk1 echo "[Log] Burn Rootfs to rootfsA (p3)" # 检查文件 if [ ! -f "rootfs.tar" ]; then echo "[Log] ERROR: rootfs.tar not found!" exit 1 fi echo "[Log] Formatting rootfsA..." mkfs.ext4 -F ${DEV}p3 echo "[Log] Extracting rootfs..." mount ${DEV}p3 /mnt tar -xf rootfs.tar -C /mnt sync umount /mnt echo "[Log] Rootfs burn complete!"

运行脚本,然后验证:

mount /dev/mmcblk1p3 /mnt
ls /mnt
umount /mnt

开机测试:

开发板的拨码开关,选择 emmc 启动,开机

p6 数据分区 ,没挂载,所以看不到。
数据区 (独立分区6) 设置为开机自动挂载 :
mkdir -p /data
mount /dev/mmcblk1p6 /data
echo "/dev/mmcblk1p6 /data ext4 defaults 0 0" >> /etc/fstab
cat /etc/fstab

目录

用途

/data

数据区,随便读写

/data/lost+found

ext4 文件系统修复后找回的损坏文件碎片,不要手动动它

以后,你的应用数据、日志、用户文件全部放 /data 目录下,不是 /data/lost+found
lost+found 是每个 ext4 分区格式化后自动生成的系统目录,正常使用不用管。

home 目录 和 data 目录的区别:

目录

位置

用途

/home

p6(rootfs A)

根文件系统,系统的一部分,升级会被覆盖

/data

p8(data)

独立分区,升级不丢

补充说明:刚才设置的data分区 开机自动挂载,只在当前根文件系统生效,如果烧录新的根文件系统,这个开机自动挂载会失效,被新的rootfs覆盖。设置永久自动挂载的方法是写入buildroot的overlay,在后面的OTA升级系统 博客文章里,有参考设置方法。

因为是自己调试用,烧录环节,分成3个脚本烧录,方便更新指定的文件,也可以整合为1个脚本,一次烧录全部系统。

关于升级:
正常启动 → rootfs A
OTA 升级 → 新系统写到 rootfs B → 切换启动 → rootfs B
下次升级 → 新系统写到 rootfs A → 切换启动 → rootfs A

系统运行时,当前根分区处于占用状态,无法完整重刷镜像,因此升级包先写入闲置备用分区,完成后再切换启动分区。

U-Boot、内核设备树、根文件系统 全部做双备份,逻辑和手机OTA升级一致:在线升级完成后,重启设备,运行新版本系统;若升级启动失败,设备回滚至旧系统正常运行。

从上篇博客到这篇博客,最终成果:

脚本用途
00_part.sh分区
01_burn_uboot.sh烧 U-Boot + 设启动
02_burn_kernel.sh烧内核 + dtb
03_burn_rootfs.sh烧根文件系统

从零到完整 A/B 双系统,全部搞定。