SPL A/B分区启动详解:Rockchip平台Android设备无缝更新

半导体产业 12 次阅读
摘要:本文详细解析了SPL阶段A/B分区启动的代码逻辑,包括元数据处理、槽位选择、启动失败处理等核心功能,帮助开发者理解并优化嵌入式系统的启动流程。

在嵌入式系统(尤其是Rockchip平台Android设备)中,A/BSeamless Update)无缝更新是保障系统更新不丢数据、更新失败可回滚的核心机制。而SPLSecondary Program Loader,二级程序加载器)作为系统启动的早期阶段,负责初始化硬件、选择启动分区,spl_ab.c正是SPL层处理A/B分区启动的核心代码。本文将从函数解析、核心流程、开发意义三个维度,彻底拆解这段代码。

一、A/B分区与SPL的核心作用

A/B分区将系统分为两个独立的槽位(Slot A/Slot B),更新时先更新非当前启动的槽位,更新完成后切换槽位启动;若启动失败,自动回退到原槽位。

SPLBootLoader的早期阶段,执行优先级最高,spl_ab.c的核心目标是:读取A/B元数据、判断槽位可启动性、选择最优启动槽位、处理启动失败后的尝试次数递减与系统重置

二、核心函数分类解析

代码中的函数可分为7大类,覆盖基础工具元数据处理槽位管理启动流程全链路,以下是关键函数的作用拆解:

1.基础工具函数:解决通用问题

函数名

核心作用

safe_memcmp

安全的内存比较,无数据依赖分支(避免侧信道攻击),返回两内存区域是否不相等(0=相等,非0=不等)

htobe32

主机字节序大端字节序转换(A/B元数据存储为大端)

be32toh

大端字节序主机字节序转换(读取元数据后适配本地CPU

2. A/B元数据处理:校验/更新/初始化

A/B元数据(AvbABData)存储在misc分区,包含槽位优先级、剩余尝试次数、启动成功标记等关键信息,这组函数是元数据操作的核心:

函数名

核心作用

spl_ab_data_verify_and_byteswap

校验元数据合法性:1.检查魔术字(AVB_AB_MAGIC)是否正确;
2.
转换CRC32为主机序;
3.
检查版本兼容性(主版本不超过支持值);
4.
校验CRC32(排除元数据损坏);
校验通过则拷贝并转换字节序到目标结构体

spl_ab_data_update_crc_and_byteswap

更新元数据的CRC32:先拷贝数据,再计算CRC32并转换为大端序(用于写入存储)

spl_ab_data_init

初始化默认元数据:
-
魔术字、版本号初始化;
- Slot A
优先级最高,Slot B次之;
-
两槽位剩余尝试次数设为最大值,启动成功标记置0

3.元数据读写:对接存储层

函数名

核心作用

spl_read_ab_metadata

misc分区指定偏移读取元数据到内存(单次读512字节,适配块设备读写粒度)

spl_write_ab_metadata

将内存中的元数据写入misc分区指定偏移

spl_ab_data_read

封装读取+校验:读取失败/校验失败时,初始化新元数据并写入misc分区

spl_ab_data_write

封装更新CRC+写入:先更新CRC32,再写入存储

4.槽位选择:核心决策逻辑

函数名

核心作用

spl_slot_is_bootable

判断槽位是否可启动:优先级>0且(已成功启动 或 剩余尝试次数>0

spl_get_lastboot

获取上次启动的槽位索引(0=A1=B

spl_get_current_slot

选择当前要启动的槽位(核心函数):1.优先使用缓存的last_slot_index(避免重复计算);
2.
读取并校验元数据;
3.
按规则选槽位:
-
两槽位都可启动:选优先级高的(同优先级选A);
-
仅一个可启动:选该槽位;
-
都不可启动:回退到上次启动的槽位;
4.
缓存结果并返回槽位后缀(_a/_b

5.分区名处理:适配槽位后缀

函数名

核心作用

spl_ab_append_part_slot

给分区名追加槽位后缀(如bootboot_a);misc分区例外(无后缀);获取槽位失败时直接返回原分区名

6.槽位状态管理:处理启动失败

函数名

核心作用

spl_slot_set_unbootable

标记槽位为不可启动:优先级、剩余尝试次数、启动成功标记全置0

spl_slot_normalize

规范化槽位状态(处理非法场景):-优先级>0但尝试次数=0且未成功置为不可启动;
-
优先级>0但尝试次数>0且已成功置为不可启动;
-
优先级≤0→直接置为不可启动

spl_ab_decrease_tries

启动失败时减少当前槽位尝试次数:
1.
获取当前槽位;
2.
读取并规范化两槽位状态;
3.
若当前槽位未成功且有剩余尝试次数,减1
4.
元数据变化则写入misc分区

spl_ab_decrease_reset

启动失败后重置系统:
1.
检查是否有可启动槽位;
2.
调用spl_ab_decrease_tries减尝试次数;
3.
仍有可启动槽位则执行系统重置,无则返回错误

7.启动参数传递:对接内核

函数名

核心作用

spl_ab_bootargs_append_slot

给设备树(FDT)的启动参数(bootargs)追加槽位后缀(如android.slot_suffix=_a),让内核感知当前启动槽位

三、SPL阶段A/B启动核心流程图

wKgZPGluucuACMd5AAEAMsHxd54329.png

四、开发者关注这段代码的核心意义

对于嵌入式开发者(尤其是Rockchip/Android BootLoader开发者),理解spl_ab.c是保障A/B启动稳定的关键,核心价值体现在5个方面:

1.快速定位启动故障

当设备出现“A/B启动失败、卡在SPL阶段、槽位切换异常时,可通过代码日志(如“CRC32 does not match”“No bootable slots found”)定位根因:

元数据CRC不匹配:misc分区损坏,spl_ab_data_read会自动初始化元数据;

无可用槽位:两槽位尝试次数耗尽,需手动重置元数据;

槽位被标记为不可启动:检查spl_slot_normalize是否触发了非法状态处理。

2.定制A/B更新策略

默认逻辑可根据产品需求调整:

调整默认优先级/尝试次数:修改spl_ab_data_init中的AVB_AB_MAX_PRIORITY/AVB_AB_MAX_TRIES_REMAINING

自定义槽位选择规则:修改spl_get_current_slot(如优先级相同时选上次启动的槽位,而非默认的Slot A);

调整启动失败后的行为:修改spl_ab_decrease_reset(如增加重试次数阈值,或取消自动重置)。

3.适配不同硬件平台

不同存储设备(eMMC/NAND/SD卡)的块设备读写(blk_dread/blk_dwrite)逻辑有差异,需确保spl_read/write_ab_metadata适配硬件;不同CPU架构的字节序可能不同,需验证htobe32/be32toh的正确性。

4.提升系统稳定性

safe_memcmp避免侧信道攻击,提升元数据比较的安全性;

spl_save_metadata_if_changed仅在元数据变化时写入存储,减少misc分区的写操作,延长存储寿命;

spl_slot_normalize处理非法状态,避免因元数据异常导致的启动逻辑崩溃。

5.适配Android无缝更新标准

Android A/B无缝更新的核心是槽位管理,这段代码是SPL层对接Android A/B规范的关键,确保更新后能正确切换槽位启动,失败时自动回滚,符合GoogleA/B更新标准。

五、总结

spl_ab.cSPL阶段A/B分区启动的大脑,从元数据读写、槽位决策到启动失败处理,覆盖了A/B启动的全核心流程。对于嵌入式开发者而言,理解这段代码不仅能快速定位启动故障,还能根据产品需求定制更新策略,保障设备在无缝更新场景下的稳定性与兼容性。

无论是调试A/B启动问题,还是适配新硬件平台,spl_ab.c都是必须深入掌握的核心模块——它是连接硬件初始化与系统启动的关键桥梁,也是保障Android无缝更新落地的基础。


评论区

登录后即可参与讨论

立即登录