我的前一篇文章《Android 启动加载器分析 —— Aboot》中分析了高通平台前代的 Aboot 的整体启动流程和相应的代码。
在 2016 年之后,MSM8996(Snapdragon 820)平台的 PBL 加载全新的 XBL,紧接着链式加载 ABL 或 Aboot(仅对于 XBL 还未成熟的 MSM8996 平台),这个程序是基于 EDK II 构建的用来替换 Aboot 的启动加载器,它可以根据按键组合选择留在 fastboot、使用系统 kernel 和在系统目录的 ramdisk 启动到 Android 系统、或是使用 recovery 的 ramdisk 启动到 Recovery。
本文介绍 ABL 的代码组织和大致启动流程,由于篇幅限制,对于启动 Linux 和 fastboot 的代码解析将会单独发各自的文章。
代码组织
高通平台的 ABL 源码可以在 Code Aurora Forum 处找到。
ABL 是基于 EDK II 构建的,整体的项目结构是标准的 EDK II 源码树。本文使用成文时的 uefi.lnx.3.0.r1 分支来分析,提交为 c4da6fcb959fa67cb2aa89007beebfab66226268。
在项目的 Makefile 中,最重要的两个构建目标是:
1 | ABL_FV_IMG := $(BUILD_ROOT)/FV/abl.fv |
其中,ABL_FV_IMG 是最重要的构建目标,它是从 QcomModulePkg 构建的 firmware(fv,固件):
1 | ABL_FV_IMG: EDK_TOOLS_BIN |
而 ABL_FV_ELF 则只是调用 QcomModulePkg/Tools/image_header.py 来把 abl.fv 转换为一个 ELF 文件,可以刷写到设备的 EMMC 或闪存中的 abl 分区。
在 QcomModulePkg 中,固件的入口是 FV.FVMAIN_COMPACT,它包含了 FV.FVMAIN,在这个模块里,囊括了 EDK II 提供的基础 ARM 栈、MMU 等组件,并包含了 QcomModulePkg/Application/LinuxLoader/LinuxLoader.inf 这个 Linux 的加载器,用来在 ARM 平台上将这个固件启动并加载 Android 的 Linux 内核。
这个 LinuxLoader 是一个 UEFI 应用程序,它的程序入口点定义如下:
1 | ENTRY_POINT = LinuxLoaderEntry |
它包含了 EDK II 中的一些模块:
1 | [Packages] |
其中,在 QcomModulePkg 的 Library 中,有 FastbootLib、BootLib、zlib 等库,Fastboot 的实现就在 FastbootLib 中,而 BootLib 中包含了启动 Linux 内核的具体实现,至于 zlib 则是因为 Linux 内核有时是被压缩了的。
启动流程
在 LinuxLoaderEntry 入口点的函数中,程序首先调用一些基础的平台代码设置环境,然后通过 DeviceInfoInit () 获取启动验证状态和设备状态,再使用 EnumeratePartitions () 和 UpdatePartitionEntries () 获取并更新分区信息。
如果存在多个启动 slot 的话(这里指有 A/B 分区的 Android 设备,其中 A 和 B 一般即为两个 slot),就寻找已激活的 slot 并记录。
紧接着获取按键状态,在 SCAN_DOWN 按下时设置启动至 fastboot 的标识,SCAN_UP 为启动至 recovery 的表示,而 SCAN_ESC 按下时则重启设备至 Emergency Download(EDL)模式。然后程序获取重启的原因并设置相应标识。
在不启动至 fastboot 时,加载并验证启动镜像,若加载并验证成功,则调用 BootLinux (&Info) 启动 Linux 内核。否则调用 FastbootInitialize () 初始化并运行 fastboot。
构建
构建 EDK II 的基础工具需要主机指令集的 GCC 工具集,而 QcomModulePkg 需要 LLVM 和 CLANG,因此需要安装这两个工具。之后使用以下命令编译:
1 | CLANG_PREFIX=aarch64-linux-gnu- PYTHON_COMMAND=python2 make |
这里由于我的系统 python 指向的是 python3,而 EDK II 基础工具集需要 python2,因此我指定了使用 python2 为默认的解释器。
总结
本文分析了 ABL 的项目结构和整体启动流程。下篇文章中会讨论正常启动 Linux(也包含 recovery 的启动)的代码流程。