cd /work/linuxecho "===== android_ab.c key words ====="
grep -RIn "slot\|suffix\|misc\|priority\|tries\|successful\|magic\|AB0" \
u-boot/common/android_ab.cecho "===== bootargs slot suffix ====="
grep -RIn "android_slotsufix\|androidboot.slot_suffix\|slot_suffix\|slotsufix" \
u-boot/common u-boot/cmd u-boot/arch/arm/mach-rockchip u-boot/include \
2>/dev/nullecho "===== partition slot logic ====="
grep -RIn "system_a\|system_b\|slot_suffix\|part_get_info\|android_part_get_info" \
u-boot/disk u-boot/common u-boot/cmd u-boot/arch/arm/mach-rockchip \
2>/dev/nullecho "===== important code ranges ====="
nl -ba u-boot/disk/part.c | sed -n '680,740p'
nl -ba u-boot/include/configs/evb_rk3568.h | sed -n '1,100p'
nl -ba u-boot/common/android_bootloader.c | sed -n '930,990p'
1. 本文目的
本文用于记录在 Rockchip RK3566 SDK 中,如何通过查找 U-Boot 源码,验证当前系统为什么要使用 system_a / system_b 作为双 RootFS 分区名,以及 U-Boot 是如何利用 Android A/B slot 机制选择当前启动槽位的。
本文基于当前源码检索结果整理,主要涉及以下文件:
u-boot/common/android_ab.c
u-boot/common/android_bootloader.c
u-boot/disk/part.c
u-boot/common/spl/spl_ab.c
u-boot/common/spl/spl_fit.c
u-boot/cmd/bootfit.c
u-boot/cmd/boot_android.c
u-boot/include/android_ab.h
u-boot/include/android_bootloader_message.h
u-boot/include/android_avb/rk_avb_ops_user.h
u-boot/include/android_image.h
核心结论如下:
当前 RK3566 U-Boot 已启用 Android A/B slot 相关逻辑。U-Boot 启动时会获取当前 slot 后缀
_a或_b,并在分区查找阶段将基础分区名追加当前 slot 后缀,从而查找类似system_a / system_b这样的分区。因此,Buildroot 双 RootFS 分区命名为system_a / system_b,是为了复用 Rockchip U-Boot 已经实现的 Android A/B 分区选择机制。
2. 验证入口:U-Boot 中确实存在 A/B slot 逻辑
本次源码检索首先针对 U-Boot 中的 A/B 相关关键字进行搜索:
cd /work/linuxgrep -RIn "slot\|suffix\|misc\|priority\|tries\|successful\|magic\|AB0" \
u-boot/common/android_ab.c
搜索结果显示,u-boot/common/android_ab.c 中存在大量 A/B slot metadata 相关字段,例如:
priority
tries_remaining
successful_boot
slot_suffix
magic
misc
其中比较关键的结果包括:
37: const struct android_slot_metadata metadata = {
38: .priority = 15,
39: .tries_remaining = 7,
40: .successful_boot = 0,
44: memcpy(abc->slot_suffix, "a\0\0\0", 4);
45: abc->magic = ANDROID_BOOT_CTRL_MAGIC;
47: abc->nb_slot = ARRAY_SIZE(abc->slot_info);
这说明 U-Boot 中维护了 A/B slot 的 metadata。每个 slot 会有:
| 字段 | 含义 |
|---|---|
priority |
slot 优先级,优先级越高越优先启动 |
tries_remaining |
剩余启动尝试次数 |
successful_boot |
该 slot 是否已经成功启动过 |
verity_corrupted |
slot 是否损坏或校验失败 |
slot_suffix |
当前选择的 slot 标识 |
magic |
A/B metadata 魔数,用于判断 metadata 是否有效 |
这与 Android A/B OTA 的基本机制一致:新系统写入 inactive slot 后,bootloader 会尝试启动新 slot;如果新系统未被确认成功启动,启动尝试次数会逐步减少;如果次数耗尽,则该 slot 会被认为不可启动,从而回退到另一个 slot。
3. A/B metadata 的默认值与 slot 状态
在 android_ab.c 中可以看到默认 slot metadata:
37: const struct android_slot_metadata metadata = {
38: .priority = 15,
39: .tries_remaining = 7,
40: .successful_boot = 0,
这说明默认情况下,slot 具备较高优先级和一定次数的启动尝试机会,但 successful_boot 初始为 0。
可以这样理解:
priority = 15表示该 slot 具有较高启动优先级。tries_remaining = 7表示该 slot 还允许尝试启动 7 次。successful_boot = 0表示该 slot 尚未被系统确认启动成功。
这对于 OTA 很重要。正常 OTA 流程中,新系统写入 inactive slot 后,bootloader 会尝试启动新 slot。如果系统成功启动,用户态需要调用类似 mark-successful 的动作,将该 slot 标记为成功。如果没有标记成功,bootloader 会继续减少 tries_remaining,最终可能回滚。
4. U-Boot 如何选择当前 slot
检索结果中有一组关键逻辑:
138:static int android_ab_compare_slots(const struct android_slot_metadata *a,
139: const struct android_slot_metadata *b)
141: /* Higher priority is better */
142: if (a->priority != b->priority)
143: return b->priority - a->priority;
145: /* Higher successful_boot value is better, in case of same priority. */
146: if (a->successful_boot != b->successful_boot)
147: return b->successful_boot - a->successful_boot;
149: /* Higher tries_remaining is better to ensure round-robin. */
150: if (a->tries_remaining != b->tries_remaining)
151: return b->tries_remaining - a->tries_remaining;
这说明 U-Boot 选择 slot 时主要比较三个指标:
prioritysuccessful_boottries_remaining
选择规则可以概括为:
优先选择 priority 更高的 slot;
如果 priority 相同,优先选择 successful_boot 为真的 slot;
如果 successful_boot 也相同,优先选择 tries_remaining 更多的 slot。
对应代码注释也能说明:
198: * We select a with the higher priority slot that
200: * - either has tries_remaining > 0 or successful_boot is true.
201: * If the slot selected has a false successful_boot, we also decrement
202: * the tries_remaining until it eventually becomes unbootable because
203: * tries_remaining reaches 0.
也就是说,U-Boot 会选择一个可启动 slot。如果该 slot 还没有被标记为成功启动,则会减少其剩余尝试次数:
239: if (slot >= 0 && !abc->slot_info[slot].successful_boot) {
240: printf("ANDROID: Attempting slot %c, tries remaining %d\n",
241: ANDROID_BOOT_SLOT_NAME(slot),
242: abc->slot_info[slot].tries_remaining);
243: abc->slot_info[slot].tries_remaining--;
这正是 A/B OTA 支持失败回滚的基础。
5. U-Boot 从哪里读取 A/B 信息:misc 分区
检索结果多次出现 misc 分区:
63: * @part_info: partition in 'dev_desc' where to read from, normally the "misc"
273:int read_misc_virtual_ab_message(struct misc_virtual_ab_message *message)
293: debug("%s: Could not found misc partition\n",
307:int write_misc_virtual_ab_message(struct misc_virtual_ab_message *message)
327: debug("%s: Could not found misc partition\n",
同时在 android_ab.c 中还有:
291: ret = part_get_info_by_name(dev_desc, PART_MISC, &part_info);
325: ret = part_get_info_by_name(dev_desc, PART_MISC, &part_info);
这说明 U-Boot 会通过分区名 misc 找到 misc 分区,并从中读取或写入 A/B 相关 metadata。
因此,在前面进行分区改造时保留 misc 分区是必要的。它不是 RootFS,也不是用户数据分区,而是 bootloader 用来保存启动状态、slot 信息、启动尝试次数等数据的地方。
6. 当前 slot 后缀的获取:ab_get_slot_suffix()
检索结果显示:
444:int ab_get_slot_suffix(char *slot_suffix)
446: /* TODO: get from pre-loader or misc partition */
447: if (rk_avb_get_current_slot(slot_suffix)) {
448: printf("rk_avb_get_current_slot() failed\n");
452: if (slot_suffix[0] != '_') {
454: printf("###There is no bootable slot, bring up lastboot!###\n");
456: memcpy(slot_suffix, "_b", 2);
458: memcpy(slot_suffix, "_a", 2);
这段非常关键。
它说明 U-Boot 会调用:
rk_avb_get_current_slot(slot_suffix)
来获取当前 slot 后缀。正常情况下,结果应该是:
_a
或:
_b
如果获取到的 slot_suffix 不是以下划线开头,U-Boot 还会执行 fallback 逻辑,尝试回到上一次可用 slot。
这说明当前 slot 后缀不是 Linux 自己决定的,而是在 U-Boot 启动阶段已经被确定。
7. U-Boot 如何把当前 slot 传给 Linux
继续搜索启动参数相关关键字:
grep -RIn "android_slotsufix\|androidboot.slot_suffix\|slot_suffix\|slotsufix" \
u-boot/common u-boot/cmd u-boot/arch/arm/mach-rockchip u-boot/include \
2>/dev/null
结果中最关键的是:
u-boot/common/android_bootloader.c:949: /* Get current slot_suffix */
u-boot/common/android_bootloader.c:950: if (ab_get_slot_suffix(slot_suffix))
u-boot/common/android_bootloader.c:1058: env_set("android_slotsufix", slot_suffix);
u-boot/common/android_bootloader.c:1075: command_line = android_assemble_cmdline(slot_suffix, mode_cmdline);
其中:
env_set("android_slotsufix", slot_suffix);
说明 U-Boot 会把当前 slot 后缀写入环境变量 android_slotsufix。
这也解释了为什么在 Linux 中执行:
cat /proc/cmdline
可以看到类似:
android_slotsufix=_a
需要注意的是,android_slotsufix 这个名字里 suffix 少了一个 f,看起来像拼写错误,但这是 Rockchip U-Boot 代码中实际使用的字段名。因此在本项目中应以实际板端 /proc/cmdline 中看到的字段为准。
8. android_bootloader.c 中的启动流程
打印 u-boot/common/android_bootloader.c 的 930 到 990 行后,可以看到:
930 printf("Could not find misc partition\n");
937 mode = android_bootloader_load_and_clear_mode(dev_desc,
938 &misc_part_info);
947 printf("ANDROID: reboot reason: \"%s\"\n", android_boot_mode_str(mode));
948 #ifdef CONFIG_ANDROID_AB
949 /* Get current slot_suffix */
950 if (ab_get_slot_suffix(slot_suffix))
951 return -1;
952 #endif
这段说明 U-Boot 启动时会先处理启动模式,例如 normal、recovery、bootloader 等,然后在启用 CONFIG_ANDROID_AB 时获取当前 slot 后缀。
接着:
953 switch (mode) {
954 case ANDROID_BOOT_MODE_NORMAL:
...
982 #ifdef CONFIG_ANDROID_AB
983 boot_partname = ab_can_find_recovery_part() ?
984 ANDROID_PARTITION_RECOVERY : ANDROID_PARTITION_BOOT;
这说明 U-Boot 会根据启动模式决定加载 boot 还是 recovery,同时 A/B 情况下有特殊处理。
对于本文关注的 Buildroot A/B RootFS 来说,重点不是 Android 正常启动流程本身,而是:
U-Boot 已经获取 slot_suffix;
U-Boot 会把 slot_suffix 写入启动参数;
后续分区查找逻辑会利用 slot_suffix。
9. 最关键的分区查找逻辑:part_get_info_by_name_option()
本次验证中最重要的代码来自:
u-boot/disk/part.c
打印 680 到 740 行:
nl -ba u-boot/disk/part.c | sed -n '680,740p'
得到关键代码:
682 static int part_get_info_by_name_option(struct blk_desc *dev_desc,
683 const char *name,
684 disk_partition_t *info,
685 bool strict)
686 {
687 struct part_driver *part_drv;
688 char name_slot[32] = {0};
689 int none_slot_try = 1;
690 int ret, i;
这里定义了一个关键变量:
char name_slot[32] = {0};
它用于保存追加了 slot 后缀之后的分区名。
10. 如果调用方已经带 _a/_b,U-Boot 会先去掉后缀
继续看:
702 #if defined(CONFIG_ANDROID_AB) || defined(CONFIG_SPL_AB)
703 char *name_suffix = (char *)name + strlen(name) - 2;
704
705 /* Fix can not find partition with suffix "_a" & "_b". If with them, clear */
706 if (!memcmp(name_suffix, "_a", strlen("_a")) ||
707 !memcmp(name_suffix, "_b", strlen("_b")))
708 memset(name_suffix, 0, 2);
709 #endif
这段逻辑说明:
如果传入的分区名本身已经带
_a或_b后缀,U-Boot 会先把这个后缀清掉。
例如传入:
system_a
可能会先变成:
system
然后再由当前 slot 统一追加后缀。
这样做的目的通常是避免出现重复后缀,例如:
system_a_a
也就是说,在 A/B 模式下,U-Boot 更倾向于使用“基础分区名 + 当前 slot 后缀”的统一规则,而不是让上层到处手动传 system_a 或 system_b。
11. A/B 模式下,U-Boot 会追加当前 slot 后缀
最关键的代码是:
710 #if defined(CONFIG_ANDROID_AB) && !defined(CONFIG_SPL_BUILD)
711 /* 1. Query partition with A/B slot suffix */
712 if (rk_avb_append_part_slot(name, name_slot))
713 return -1;
注释已经写得很清楚:
Query partition with A/B slot suffix
也就是:
查询分区时,优先使用带 A/B slot 后缀的分区名。
虽然本次输出还没有展开 rk_avb_append_part_slot() 的函数定义,但从函数名和上下文已经能看出它的目的:
rk_avb_append_part_slot(name, name_slot)
即根据当前 A/B slot,把基础分区名 name 转换成带 slot 后缀的 name_slot。
可以理解为:
name = "system"
slot_suffix = "_a"
name_slot = "system_a"
或:
name = "system"
slot_suffix = "_b"
name_slot = "system_b"
12. U-Boot 用追加后缀后的名字查找分区表
后面真正查分区表的是这一段:
720 lookup:
721 debug("## Query partition(%d): %s\n", none_slot_try, name_slot);
722 for (i = 1; i < part_drv->max_entries; i++) {
723 ret = part_drv->get_info(dev_desc, i, info);
724 if (ret != 0) {
725 /* no more entries in table */
726 break;
727 }
728 if (strcmp(name_slot, (const char *)info->name) == 0) {
729 /* matched */
730 return i;
731 }
732 }
这说明 U-Boot 最终会拿 name_slot 与分区表中的分区名进行比较:
strcmp(name_slot, info->name)
因此,如果当前 slot 是 _a,并且基础分区名为 system,那么 U-Boot 会查找:
system_a
如果当前 slot 是 _b,则查找:
system_b
这就解释了为什么 parameter-buildroot-fit.txt 中必须定义:
system_a
system_b
而不是随便定义成其他名字。
13. 如果带 slot 后缀的分区找不到,U-Boot 会回退查基础分区名
代码后面还有 fallback 逻辑:
734 /* 2. Query partition without A/B slot suffix if above failed */
735 if (none_slot_try) {
736 none_slot_try = 0;
737 strcpy(name_slot, name);
738 goto lookup;
739 }
这说明 U-Boot 的查找流程是:
第一步:先查带 slot 后缀的分区名,例如 system_a 或 system_b;
第二步:如果查不到,再尝试查原始分区名,例如 system。
这是一种兼容设计。
也就是说,开启 A/B 后,如果分区表中存在 system_a/system_b,就会优先命中带后缀的分区。如果不存在,U-Boot 可能还会尝试查普通 system 分区。
但对于 A/B OTA 来说,应该明确提供 system_a/system_b,这样才能让 slot 选择真正生效。
14. SPL 阶段也存在 A/B 后缀处理
搜索结果中还出现了 SPL 相关逻辑:
u-boot/common/spl/spl_ab.c:258: char slot_suffix[3] = {0};
u-boot/common/spl/spl_ab.c:265: if (spl_get_current_slot(dev_desc, "misc", slot_suffix)) {
u-boot/common/spl/spl_ab.c:272: strcat(new_name, slot_suffix);
u-boot/common/spl/spl_ab.c:292: char slot_suffix[3] = {0};
u-boot/common/spl/spl_ab.c:295: ret = spl_get_current_slot(dev_desc, "misc", slot_suffix);
u-boot/common/spl/spl_ab.c:299: if (!strncmp(slot_suffix, "_a", 2))
u-boot/common/spl/spl_ab.c:301: else if (!strncmp(slot_suffix, "_b", 2))
这里更加直接地出现了:
strcat(new_name, slot_suffix);
这说明在 SPL 阶段,也存在将分区基础名和 slot 后缀拼接的逻辑。
虽然我们当前 Buildroot RootFS 的主要验证点是在 U-Boot 阶段,但 SPL 代码进一步说明 Rockchip 的启动链路中确实使用 _a/_b 作为 A/B slot 命名规则。
15. FIT 启动参数中也会追加 A/B 信息
搜索结果中还有:
u-boot/common/spl/spl_fit.c:541: char slot_suffix[3] = {0};
u-boot/common/spl/spl_fit.c:543: if (!spl_get_current_slot(info->dev, "misc", slot_suffix))
u-boot/common/spl/spl_fit.c:544: fdt_bootargs_append_ab((void *)image_info.load_addr, slot_suffix);
这说明在 SPL FIT 镜像加载过程中,也会获取当前 slot 后缀,并通过:
fdt_bootargs_append_ab(...)
把 A/B 信息追加到设备树或启动参数中。
这与我们在 Linux 中看到的:
android_slotsufix=_a
属于同一类现象,即 bootloader 在启动 Linux 之前,已经把当前 A/B slot 信息写入了启动参数。
16. bootfit.c 中也会追加 android_slotsufix
搜索结果中还有:
u-boot/cmd/bootfit.c:98: char slot_suffix[3] = {0};
u-boot/cmd/bootfit.c:99: char slot_info[21] = "android_slotsufix=";
u-boot/cmd/bootfit.c:101: if (ab_get_slot_suffix(slot_suffix))
u-boot/cmd/bootfit.c:104: strcat(slot_info, slot_suffix);
这说明在 bootfit 启动命令中,也会拼接:
android_slotsufix=_a
或:
android_slotsufix=_b
也就是说,不止 android_bootloader.c,Rockchip 的 FIT 启动路径中也有 A/B slot 信息传递逻辑。
17. boot_android.c 中明确存在 _a/_b 后缀数组
搜索结果还显示:
u-boot/cmd/boot_android.c:547: const char * slot_suffixes[2] = {"_a", "_b"};
u-boot/cmd/boot_android.c:563: slot_suffixes[n],
这说明 U-Boot 的 Android 启动命令中明确将 _a 和 _b 作为两个 slot 后缀。
这与我们在分区命名中使用:
system_a
system_b
保持一致。
18. 为什么源码中没有大量写死 system_a/system_b
在源码搜索中,直接搜索 system_a 或 system_b 并没有出现很多结果,这是正常的。
原因是 U-Boot 的 A/B 机制通常不会到处写死:
"system_a"
"system_b"
而是采用通用规则:
基础分区名 + 当前 slot 后缀
例如:
system + _a = system_a
system + _b = system_b
boot + _a = boot_a
boot + _b = boot_b
vendor + _a = vendor_a
vendor + _b = vendor_b
这种机制更通用,适配 Android A/B 体系中多个 slot 化分区。
因此,在源码中更常见的是:
slot_suffix
_a
_b
append_part_slot
part_get_info_by_name
而不是直接写死 system_a/system_b。
19. 为什么 Buildroot 也要命名成 system_a/system_b
虽然当前系统运行的是 Buildroot,而不是完整 Android,但我们复用了 Rockchip U-Boot 的 Android A/B 启动机制。
U-Boot 的分区查找逻辑是:
基础分区名 + 当前 slot suffix
如果基础分区名是 system,当前 slot 是 _a,则实际查找:
system_a
如果当前 slot 是 _b,则实际查找:
system_b
因此,Buildroot 的 RootFS 分区命名为:
system_a
system_b
本质上是为了适配 U-Boot 已有的 A/B slot 选择逻辑。
如果随便命名成:
rootfs_a
rootfs_b
那么除非同步修改 U-Boot 中查找的基础分区名为 rootfs,否则默认逻辑下可能不会正确查到这些分区。
所以本项目的设计不是重新发明一套 OTA slot 机制,而是让 Buildroot RootFS 适配 Rockchip U-Boot 已有的 Android A/B 规则。
20. 与 parameter-buildroot-fit.txt 的关系
前面已经确认,parameter-buildroot-fit.txt 中定义了:
0x00600000@0x00058000(system_a)
0x00600000@0x00658000(system_b)
以及:
uuid:system_a=614e0000-0000-4b53-8000-1d28000054a9
uuid:system_b=614e0001-0000-4b53-8000-1d28000054a9
这说明板端分区表中实际存在:
system_a
system_b
而 U-Boot 源码中又存在:
基础分区名 + slot suffix
的分区查找逻辑。
因此两者是配合关系:
U-Boot 当前 slot = _a↓
U-Boot 查找 system_a↓
parameter 中存在 system_a↓
成功找到 A 槽 RootFSU-Boot 当前 slot = _b↓
U-Boot 查找 system_b↓
parameter 中存在 system_b↓
成功找到 B 槽 RootFS
21. 与 rk356x-package-file-ab-rootfs 的关系
前面已经确认,rk356x-package-file-ab-rootfs 中包含:
system_a Image/rootfs.img
system_b Image/rootfs.img
它的作用不是决定 U-Boot 怎么选择 slot,而是在完整固件打包阶段告诉 Rockchip 打包工具:
把 Image/rootfs.img 写入 system_a;
把 Image/rootfs.img 也写入 system_b。
这样首次完整烧录后,两个 RootFS 分区中都有系统文件。
三者关系可以总结为:
parameter-buildroot-fit.txt定义 system_a / system_b 分区存在。rk356x-package-file-ab-rootfs把 rootfs.img 写入 system_a / system_b。U-Boot A/B slot 逻辑根据当前 slot 选择 system_a 或 system_b 启动。
22. 当前验证链路总结
根据本次源码检索,可以形成如下完整证据链:
1. U-Boot 中存在 CONFIG_ANDROID_AB 相关逻辑↓
2. android_ab.c 中存在 slot metadata↓
3. metadata 包含 priority / tries_remaining / successful_boot↓
4. U-Boot 会根据 priority、successful_boot、tries_remaining 选择可启动 slot↓
5. ab_get_slot_suffix() 会获取当前 slot 后缀 _a 或 _b↓
6. android_bootloader.c 会将 slot_suffix 写入 android_slotsufix↓
7. Linux 启动后 /proc/cmdline 可看到 android_slotsufix=_a 或 _b↓
8. part.c 的 part_get_info_by_name_option() 在 A/B 模式下会调用 rk_avb_append_part_slot()↓
9. U-Boot 查分区时优先使用带 A/B slot 后缀的分区名↓
10. 基础分区名 system 在 _a 槽下对应 system_a,在 _b 槽下对应 system_b↓
11. 因此 Buildroot 双 RootFS 分区命名为 system_a/system_b 是为了适配 Rockchip U-Boot 的 A/B slot 机制
23. 当前还缺的最后一个源码闭环
本次结果已经证明:
part_get_info_by_name_option()调用了 rk_avb_append_part_slot(name, name_slot)
但还没有展开 rk_avb_append_part_slot() 的函数定义。
为了让证据链更完整,建议继续在 VSCode 或命令行中搜索:
cd /work/linuxgrep -RIn "rk_avb_append_part_slot" u-boot 2>/dev/null
找到函数定义后,重点看它是否存在类似逻辑:
strcpy(name_slot, name);
strcat(name_slot, slot_suffix);
或:
snprintf(name_slot, ..., "%s%s", name, slot_suffix);
如果能看到这类代码,就可以完全确认:
system + _a = system_a
system + _b = system_b
不过,即使当前还没有展开这个函数定义,结合 part.c 中的注释:
Query partition with A/B slot suffix
以及 SPL 中已经出现的:
strcat(new_name, slot_suffix)
也已经足以说明 Rockchip U-Boot 使用的是“基础分区名 + A/B slot 后缀”的规则。
24. 推荐补充验证命令
为了进一步完善文档,建议继续执行以下命令:
cd /work/linuxgrep -RIn "rk_avb_append_part_slot" u-boot 2>/dev/null
如果找到定义,例如在某个文件中显示:
u-boot/xxx/yyy.c:123:int rk_avb_append_part_slot(...)
则继续执行:
nl -ba u-boot/xxx/yyy.c | sed -n '110,150p'
还可以继续查 rk_avb_get_current_slot():
grep -RIn "rk_avb_get_current_slot" u-boot 2>/dev/null
重点确认:
当前 slot 后缀来自哪里;
是否读取 misc / avb_ab metadata;
是否会返回 _a 或 _b。
25. 文档中可使用的最终表述
可以将以下内容直接写入项目文档:
通过检索 U-Boot 源码可以确认,当前 RK3566 U-Boot 已启用 Android A/B slot 相关逻辑。`u-boot/common/android_ab.c` 中维护了 A/B slot metadata,包括 priority、tries_remaining、successful_boot、slot_suffix 等字段。U-Boot 会根据这些字段选择当前可启动 slot,并通过 `ab_get_slot_suffix()` 获取当前 slot 后缀 `_a` 或 `_b`。在 `u-boot/common/android_bootloader.c` 中,U-Boot 会调用 `ab_get_slot_suffix(slot_suffix)` 获取当前 slot,并通过 `env_set("android_slotsufix", slot_suffix)` 将 slot 信息写入启动参数。因此 Linux 启动后可以在 `/proc/cmdline` 中看到 `android_slotsufix=_a` 或 `android_slotsufix=_b`。最关键的是,在 `u-boot/disk/part.c` 的 `part_get_info_by_name_option()` 函数中,当启用 `CONFIG_ANDROID_AB` 后,U-Boot 会调用 `rk_avb_append_part_slot(name, name_slot)`,优先使用带 A/B slot 后缀的分区名进行查找。也就是说,如果基础分区名为 `system`,当前 slot 为 `_a`,则实际查找 `system_a`;当前 slot 为 `_b`,则实际查找 `system_b`。因此,Buildroot 双 RootFS 分区命名为 `system_a / system_b` 并不是随意命名,而是为了复用 Rockchip U-Boot 已有的 Android A/B slot 选择机制。
26. 本阶段结论
本阶段源码验证可以得出以下结论:
- 当前 U-Boot 源码中存在完整的 Android A/B slot 相关逻辑。
android_ab.c中维护了 slot metadata,包括优先级、启动尝试次数和启动成功标志。- U-Boot 会从 misc / AVB A/B 信息中获取当前 slot 后缀。
- 当前 slot 后缀为
_a或_b。 - U-Boot 会把 slot 后缀写入启动参数,因此 Linux 中可以看到
android_slotsufix=_a/_b。 - U-Boot 查找分区时会优先查询带 A/B slot 后缀的分区名。
- 因此,基础分区名
system在 A 槽下对应system_a,在 B 槽下对应system_b。 - Buildroot 使用
system_a/system_b命名,是为了直接复用 Rockchip U-Boot 的 Android A/B slot 机制。 parameter-buildroot-fit.txt负责定义system_a/system_b分区。rk356x-package-file-ab-rootfs负责在完整固件烧录时把rootfs.img同时写入system_a/system_b。- U-Boot A/B 逻辑负责根据当前 slot 选择对应分区启动。
一句话总结:
system_a / system_b不是为了看起来像 Android,而是因为 Rockchip U-Boot 的 A/B 分区查找逻辑就是按照“基础分区名 + slot 后缀”的方式工作的。Buildroot 这样命名,是为了让自己的 RootFS 能接入已有的 bootloader A/B 切换机制。
