RK3566 AB升级功能
2026-2-15 hongxi.zhu
RK3566 Linux SDK (kernel 4.19) 泰山派
配置分区表
1. 创建ab升级专属的分区表
bash
cd device/rockchip/rk356x/
touch parameter-buildroot-fit-ab.txt
配置内容如下(格式参考默认的parameter-buildroot-fit.txt为模版):
shell
FIRMWARE_VER: 1.0
MACHINE_MODEL: RK3568
MACHINE_ID: 007
MANUFACTURER: RK3568
MAGIC: 0x5041524B
ATAG: 0x00200800
MACHINE: 0xffffffff
CHECK_MASK: 0x80
PWR_HLD: 0,0,A,0,1
TYPE: GPT
CMDLINE: mtdparts=rk29xxnand:0x00002000@0x00004000(uboot_a),0x00002000@0x00006000(uboot_b),0x00002000@0x00008000(misc),0x00020000@0x0000a000(boot_a),0x00020000@0x0002a000(boot_b),0x00020000@0x0004a000(recovery),0x00010000@0x0006a000(backup),0x00c00000@0x0007a000(system_a),0x00c00000@0x00c7a000(system_b),0x00040000@0x0187a000(oem),-@0x018ba000(userdata:grow)
RK的分区表各项说明
FIRMWARE_VER: 1.0 |
固件版本,打包updata.img时会使用到,升级工具会根据这个识别固件版本。 |
|---|---|
MACHINE_MODEL: RK3568 |
机器型号,打包updata.img使用,不同的项目,可以自己修改,用于升级工具显示。在recovery里面升级固件时可以用于判断固件是否匹配。 |
MACHINE_ID: 007 |
产品开发ID,可以为字符和数字组合,打包updata.img使用,不同的项目使用不同的ID,可以用于识别机器机型。在recovery里面升级固件时可以用于判断固件是否匹配。 |
MANUFACTURER: RK3568 |
厂商信息,打包updata.img使用,可以自己修改,用于升级工具显示。 |
MAGIC: 0x5041524B |
MAGIC,不能修改,一些新的AP使用DTS,这一项没有用,为了兼容,不要删除或修改。 |
ATAG: 0x00200800 |
ATAG,不能修改,一些新的AP使用DTS,这一项没有用,为了兼容,不要删除或修改。 |
MACHINE: 0xffffffff |
内核识别用,不能修改,这个定义和内核匹配。 |
CHECK_MASK: 0x80 |
保留,不能修改。 |
TYPE: GPT |
指定该文件CMDLINE里面定义的分区用于创建GPT使用,不会烧录到NVM(NAND,EMMC等)存储器件里面。 |
CMDLINE |
下面说明 |
关于CMDLINE以下是摘自瑞芯微文档的说明:
- 为了兼容性,目前
RK所有AP都是用rk29xxnand做标识。 - 单个分区说明:
例如:0x00010000@0x00008000(boot),@符号之前的数值是分区大小,@符号之后的数值是分区的起始位置,括号里面的字符是分区的名字。所有数值的单位是sector,1个sector为512Bytes。上例中,boot分区起始位置为0x8000 sectors位置,大小为0x10000 sectors(32MB)。 - 为了性能,每个分区起始地址需要
32KB(64 sectors)对齐,大小也需要32KB的整数倍。 - 如果使用
sparse格式的镜像,升级时会擦除数据,为了兼容性更好,对应的分区最好按4MB对齐,大小也按4MB整数倍配置。 - 使用
GPT分区时,parameter里面定义的地址,都是真实的逻辑地址(LBA),例如uboot定义在0x4000,那么烧录到EMMC和NAND里面时,逻辑地址也是0x4000。最后一个分区需要指定grow参数,工具会把剩余的空间都分配给最后一个分区。
对于上述配置的parameter-buildroot-fit-ab.txt的内容,整理出来对应的分区layout就如下表格:
| 名称 | 起始地址 | 结束地址 | 大小 |
|---|---|---|---|
| uboot_a | 0x00004000(8M) | 0x00006000(12M) | 0x00002000(4M) |
| uboot_b | 0x00006000(12M) | 0x00008000(16M) | 0x00002000(4M) |
| misc | 0x00008000(16M) | 0x0000a000(20M) | 0x00002000(4M) |
| boot_a | 0x0000a000(20M) | 0x0002a000(84M) | 0x00020000(64M) |
| boot_b | 0x0002a000(84M) | 0x0004a000(148M) | 0x00020000(64M) |
| recovery | 0x0004a000(148M) | 0x0006a000(212M) | 0x00020000(64M) |
| backup | 0x0006a000(212M) | 0x0007a000(244M) | 0x00010000(32M) |
| system_a | 0x0007a000(244M) | 0x00c7a000(6388M) | 0x00c00000(6144M) |
| system_b | 0x00c7a000(6388M) | 0x0187a000(12532M) | 0x00c00000(6144M) |
| oem | 0x0187a000(12532M) | 0x018ba000(12660M) | 0x00040000(128M) |
| userdata | 0x018ba000(12660M) | -- | EMMC剩余容量 |
这里为啥EMMC分区要从8M的地方开始呢,因为前面的区域被系统一些固定的内容占用了,如分区表、Loader、reserved part等
| 名称 | 起始地址 | 大小 |
|---|---|---|
| GPT分区表 | 0 | 32KB |
| Loader | 0x00000040 | 4MB - 32KB |
| 保留的区域 | 0x00002000 | 4MB |
2. 创建分区镜像打包规则文件
bash
cd tools/linux/Linurk356x-package-file-ab
touch rk356x-package-file-ab
配置内容如下(格式参考默认的rk356x-package-file为模版):
bash
# NAME Relative path
#
#HWDEF HWDEF
package-file package-file
bootloader Image/MiniLoaderAll.bin
parameter Image/parameter.txt
uboot_a Image/uboot.img
uboot_b Image/uboot.img
misc Image/misc.img
boot_a Image/boot.img
boot_b Image/boot.img
recovery Image/recovery.img
system_a Image/rootfs.img
system_b Image/rootfs.img
oem Image/oem.img
userdata Image/userdata.img
backup RESERVED
内容实际上描述每个分区使用对应镜像, 当分区烧写时,将对应的镜像写入对应的分区(如果原本有数据就意味着覆盖重刷该分区)
开启AB升级相关的配置项
1. uboot
u-boot/configs/rk3568_defconfig
shell
CONFIG_AVB_LIBAVB=y
CONFIG_AVB_LIBAVB_AB=y
CONFIG_AVB_LIBAVB_ATX=y
CONFIG_AVB_LIBAVB_USER=y
CONFIG_RK_AVB_LIBAVB_USER=y
CONFIG_ANDROID_AB=y
2. kernel
kernel不需要配置
3. buildroot
buildroot/configs/rockchip_rk3566_defconfig
shell
BR2_PACKAGE_RECOVERY=y # 开启升级功能
BR2_PACKAGE_RECOVERY_BOOTCONTROL=y # 开启引导控制脚本
BR2_PACKAGE_RECOVERY_RETRY=y # 引导⽅式为retry模式,不配置则默认为successful_boot模式
BR2_PACKAGE_RECOVERY_USE_UPDATEENGINE=y # 使⽤升级程序updateEngine
BR2_PACKAGE_RECOVERY_UPDATEENGINEBIN=y # 编译升级程序updateEngine
BR2_PACKAGE_RECOVERY_NO_UI=y # 关掉UI
4. SDK
device/rockchip/rk356x/BoardConfig-rk3566-tspi-v10.mk
shell
export RK_PARAMETER=parameter-buildroot-fit-ab.txt # 使用创建的AB分区表描述文件
export RK_PACKAGE_FILE=rk356x-package-file-ab # 使用创建的AB打包规则文件
export RK_PACKAGE_FILE_AB=rk356x-package-file-ab # 使用创建的AB打包规则文件
编译&打包
bash
./build.sh # 会编译出update_ab.img
# 这个就用于AB升级
rockdev/update_ab.img
使用updateEngine升级
验证升级
-
首先先使用固件烧录工具
RKDevTool_Release烧写这个全分区镜像文件到设备上 -
查看当前运行的分区信息
-
查看方式1(updateEngine工具)
bashroot@RK356X:/# updateEngine --misc=display ... LOG_INFO :slot.[0]->priority = 14. LOG_INFO :slot.[0]->successful_boot = 0. LOG_INFO :slot.[0]->tries_remaining = 7. LOG_INFO :slot.[1]->priority = 15. LOG_INFO :slot.[1]->successful_boot = 0. LOG_INFO :slot.[1]->tries_remaining = 7. LOG_INFO :last_boot : 1. ...last_boot: 0-> slot A; 1 -> slot Bpriority: 15 -> 活跃分区;14 -> 非活跃分区因此例子这里显示启动的是B分区。
-
查看方式2(cmdline方式)
bashroot@RK356X:/# cat /proc/cmdline storagemedia= ... android_slotsufix=_b ...android_slotsufix: uboot传递给kernel的分区index,用于挂载对应slot分区的rootfs因此例子这里显示启动的是B分区。
-
查看方式3 (uboot的启动log)
bashA/B-slot: _b, successful: 0, tries-remain: 7
-
-
在根目录下创建一个标识文件(用于验证切换分区后是否升级成功)
bash
touch update_flag.txt
- 使用
updateEngine
bash
updateEngine --image_url=/mnt/udisk/update_ab.img --update --reboot
这个过程会有log, 显示相关镜像的写入和分区的详细信息
-
观察结果
重启后观察分区是否为另外一个分区,且根目录下
update_flag.txt是否存在,同时还可以使用下面的命令切换回上一个分区观察该文件是是否存在做对比。bashupdateEngine --misc=other reboot
关于引导方式的区别:
successful_boot 模式:UBoot启动时会读取A、B分区的状态信息,主要检查 successful_boot标志。它会优先引导被标记为成功(successful_boot=1)且优先级最高的分区。如果该分区启动失败,UBoot会清除其 successful_boot标志,并尝试引导另一个分区。当系统(如Linux内核)被成功加载并运行后,用户空间的守护进程(如 bootcontrol)会负责将当前分区的 successful_boot标志设置为1,表示本次启动成功。实际测试,RK平台当前successful_boot模式不支持自动切换引导分区
reset_retry 模式:UBoot读取分区状态后,会优先选择 priority最高且 tries_remaining > 0的分区进行引导,并在尝试前先将该分区的 tries_remaining减1。如果启动失败,流程回转,该分区的计数器会再次被扣除,直至降为0后,系统才会切换到另一个分区进行尝试。如果一个分区能成功启动到系统,系统服务会将该分区的 tries_remaining重置为最大值(通常是7次),并将 last_boot记录更新为当前槽位,为下一次启动积累"信任"
特性对比
| 特性维度 | successful_boot 模式 | reset_retry 模式 |
|---|---|---|
| 核心机制 | 依赖successful_boot 标志位。系统成功启动后会标记该分区为"已成功启动"。 | 依赖tries_remaining (剩余尝试次数)。为每个分区设置一个尝试次数计数器。 |
| 决策依据 | 优先选择历史上成功启动过的分区。 | 优先选择优先级最高且还有剩余尝试次数的分区。 |
| 重试行为 | 相对"宽松"。一个分区只要曾经成功过,就倾向于继续使用它。 | 相对"严格"。给新分区有限的尝试机会,若多次启动失败则果断放弃。 |
| 适用场景 | 追求启动成功率和稳定性,对启动速度不特别敏感的环境。 | 需要快速尝试并切换到新系统(如完成OTA更新后)的环境,常见于消费电子。 |
关于misc分区的内容
数据结构
misc分区的2k偏移处存放了如下结构体,用于存放 slot_a 和 slot_b 的引导信息:
u-boot/include/android_avb/avb_ab_flow.h
c
// Rockchip A/B 分区引导槽状态
/* Struct used for recording per-slot metadata.
*
* When serialized, data is stored in network byte-order.
*/
typedef struct AvbABSlotData {
/* Slot priority. Valid values range from 0 to AVB_AB_MAX_PRIORITY,
* both inclusive with 1 being the lowest and AVB_AB_MAX_PRIORITY
* being the highest. The special value 0 is used to indicate the
* slot is unbootable.
*/
uint8_t priority; // 启动优先级,0~15,0 表示禁用
/* Number of times left attempting to boot this slot ranging from 0
* to AVB_AB_MAX_TRIES_REMAINING.
*/
uint8_t tries_remaining; // 剩余启动尝试次数(最多 7 次)
/* Non-zero if this slot has booted successfully, 0 otherwise. */
uint8_t successful_boot; // 1 表示已成功启动过
/* Reserved for future use. */
uint8_t reserved[1]; // 保留(或平台自定义)
} AVB_ATTR_PACKED AvbABSlotData;
c
// Rockchip A/B 分区信息结构(存放于 misc 分区偏移 2048 处,共 32 字节)
/* Struct used for recording A/B metadata.
*
* When serialized, data is stored in network byte-order.
*/
typedef struct AvbABData {
/* Magic number used for identification - see AVB_AB_MAGIC. */
uint8_t magic[AVB_AB_MAGIC_LEN]; // 结构体头部魔术字:"\0AB0"
/* Version of on-disk struct - see AVB_AB_{MAJOR,MINOR}_VERSION. */
uint8_t version_major; // 主版本号
uint8_t version_minor; // 次版本号
/* Padding to ensure |slots| field start eight bytes in. */
uint8_t reserved1[2]; // 保留字节(对齐)
/* Per-slot metadata. */
AvbABSlotData slots[2]; // A/B 分区引导槽状态(两个槽)
/* Reserved for future use. */
uint8_t last_boot; // 上次成功启动的槽:0 表示 slot_a,1 表示 slot_b
uint8_t reserved2[11]; // 保留字节(填充到 32 字节)
/* CRC32 of all 28 bytes preceding this field. */
uint32_t crc32; // 结构体 CRC 校验(覆盖前 28 字节)
} AVB_ATTR_PACKED AvbABData;
我上面的配置misc分区是第三个分区,使用fdisk -l可以看到
bash
root@RK356X:/# fdisk -l /dev/mmcblk0
Found valid GPT with protective MBR; using GPT
Disk /dev/mmcblk0: 30535680 sectors, 2622M
Logical sector size: 512
Disk identifier (GUID): 203e0000-0000-4e6b-8000-14e0000074a2
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 30535646
Number Start (sector) End (sector) Size Name
1 16384 24575 4096K uboot_a
2 24576 32767 4096K uboot_b
3 32768 40959 4096K misc
4 40960 172031 64.0M boot_a
5 172032 303103 64.0M boot_b
6 303104 434175 64.0M recovery
7 434176 499711 32.0M backup
8 499712 13082623 6144M system_a
9 13082624 25665535 6144M system_b
10 25665536 25927679 128M oem
11 25927680 30535615 2249M userdata
# 或者通过块驱动节点查看 ls -al /dev/block/by-name/*
root@RK356X:/# ls -al /dev/block/by-name/*
lrwxrwxrwx 1 root root 15 Aug 4 09:00 /dev/block/by-name/backup -> ../../mmcblk0p7
lrwxrwxrwx 1 root root 15 Aug 4 09:00 /dev/block/by-name/boot_a -> ../../mmcblk0p4
lrwxrwxrwx 1 root root 15 Aug 4 09:00 /dev/block/by-name/boot_b -> ../../mmcblk0p5
lrwxrwxrwx 1 root root 15 Aug 4 09:00 /dev/block/by-name/misc -> ../../mmcblk0p3
lrwxrwxrwx 1 root root 16 Aug 4 09:00 /dev/block/by-name/oem -> ../../mmcblk0p10
lrwxrwxrwx 1 root root 15 Aug 4 09:00 /dev/block/by-name/recovery -> ../../mmcblk0p6
lrwxrwxrwx 1 root root 15 Aug 4 09:00 /dev/block/by-name/system_a -> ../../mmcblk0p8
lrwxrwxrwx 1 root root 15 Aug 4 09:00 /dev/block/by-name/system_b -> ../../mmcblk0p9
lrwxrwxrwx 1 root root 15 Aug 4 09:00 /dev/block/by-name/uboot_a -> ../../mmcblk0p1
lrwxrwxrwx 1 root root 15 Aug 4 09:00 /dev/block/by-name/uboot_b -> ../../mmcblk0p2
lrwxrwxrwx 1 root root 16 Aug 4 09:00 /dev/block/by-name/userdata -> ../../mmcblk0p11
整个AvbAbData结构体大小为32Byte,存储在misc分区的2KByte偏移处。可以通过dd命令查看其数据,如下是第一次烧录镜像后的misc分区数据:
bash
root@RK356X:/# dd if=/dev/mmcblk0p3 bs=1 skip=2048 count=32 | hexdump -C
00000000 00 41 42 30 01 00 00 00 0e 07 00 00 0f 07 00 00 |.AB0............|
00000010 01 00 00 00 00 00 00 00 00 00 00 00 29 5b 03 07 |............)[..|
32+0 records in
32+0 records out
00000020
32 bytes copied, 0.00150938 s, 21.2 kB/s
解析后得到结论如下:
| 字段 | 值 | 含义 |
|---|---|---|
| magic | 00 41 42 30 | "\0AB0",结构有效 |
| version | 1.0 | A/B 数据结构版本 |
| slot_a.priority | 14 | 启动优先级 |
| slot_a.tries_remaining | 7 | 剩余启动尝试次数 |
| slot_a.successful_boot | 0 | 未标记成功启动(Successful boot模式才有用) |
| slot_a.reserved | 0 | 表示升级是否成功(可选,平台自定义) |
| slot_b.priority | 15 | 启动优先级 |
| slot_b.tries_remaining | 7 | 剩余启动尝试次数 |
| slot_b.successful_boot | 0 | 未标记成功启动(Successful boot模式才有用) |
| slot_b.reserved | 0 | 表示升级是否成功(可选,平台自定义) |
| last_boot | 1 | 上次引导的是 slot_b |
| crc32 | 0x295b0307 | CRC 校验值 |
引导流程,kernel 4.19的流程比较老,网上的图不适合,关于successful_boot的逻辑不一样,后续补充,但是关于更新misc分区的地方是一样的,都是通过开机脚本设置的。
各种测试
待补充