第2章 ABL模块入口实现
2.1 入口函数原型
ABL(Android Bootloader)作为一个UEFI应用程序,其入口函数遵循UEFI驱动模型,但名称和约定与标准EDK II框架有所区分。典型ABL入口原型如下:
EFI_STATUS
ABL_ENTRY_POINT (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
习惯上,ABL代码中将该函数命名为 abl_entry 或 BootloaderEntry,具体名称由编译时的固件集成者确定。入口点通过UEFI图像加载器从 .efi 文件的PE头中解析并调用。
集成提示: 在UEFI平台的
.dsc 和 .fdf 文件中,需要将ABL的入口函数符号与固件中的 BaseMemoryLib、DevicePathLib 等库正确链接;否则调用时可能出现符号未定义错误。
2.2 参数含义详解
两个参数 ImageHandle 和 SystemTable 分别承载ABL自身的模块上下文和全局UEFI运行时服务。具体含义如下表:
| 参数 | 类型 | 含义 |
|---|---|---|
ImageHandle | EFI_HANDLE | ABL图像自身的句柄,用于获取加载的设备路径、映像类型、协议接口等。 |
SystemTable | EFI_SYSTEM_TABLE * | 指向UEFI系统表的指针,包含 BootServices、RuntimeServices、ConfigurationTable 和标准控制台输出输入。 |
在ABL实现中,入口函数第一件事通常是将 SystemTable 保存到全局变量,以便后续阶段能持续访问UEFI服务。典型的初始化片段:
EFI_STATUS
abl_entry(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
EFI_STATUS Status;
gImageHandle = ImageHandle;
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
// 注册退出启动服务回调
Status = gBS->RegisterProtocolNotify(&gEfiEventExitBootServicesGuid,
TPL_CALLBACK,
ExitBootServicesCallback);
...
}
常见陷阱: 如果ABL入口函数内未主动调用
gBS->SetWatchdogTimer(0, 0, 0, NULL) 禁用UEFI看门狗,长时间加载引导镜像可能导致系统复位。建议在入口点立即关闭看门狗。
2.3 与UEFI Entry Point的区别
标准UEFI应用程序入口(例如EDK II生成的 efi_main)与ABL入口在调用约定、资源初始化阶段、以及生命周期方面存在本质差异。下表逐项对比:
| 对比项 | UEFI标准Entry Point | ABL入口(abl_entry) |
|---|---|---|
| 调用者 | UEFI固件的图像加载器(DXE或BDS阶段) | 固件内嵌的引导管理器(BDS),部分平台通过 LoadImage + StartImage 调用 |
| 参数传递 | 纯UEFI规范:ImageHandle + SystemTable | 完全一致;但ABL通常还会在固件侧预置自定义协议或 RAM磁盘设备路径 |
| 调用约定 | EFIAPI(微软x64调用/ARM AAPCS) | 在ABL源码中可能未明确标注EFIAPI,由链接脚本保证与固件一致 |
| 生命周期 | 返回后若Status为SUCCESS,映像被卸载;否则保留句柄 | 应始终返回 EFI_SUCCESS,因为ABL必须接管启动流程;若返回错误,固件可能回退至shell或菜单 |
| 栈与堆 | 由UEFI BDS分配默认栈(通常64 KB) | ABL在入口点会重新分配更大的栈(例如256 KB)用于解压和验证 |
| 协议访问 | 通过 SystemTable->BootServices 标准接口 | 额外依赖设备路径协议、活动分区协议、存储协议(如 BlockIo、DiskIo) |
关键差异: ABL入口在返回前必须调用
EFI_BOOT_SERVICES.ExitBootServices() 以转移控制权给实际内核;而普通UEFI程序通常保持启动服务直至退出。ABL设计为“一次性引导”,不在UEFI环境中常驻。
2.4 入口点之后的初始化流程
ABL入口函数内部典型执行顺序:
- 保存SystemTable并禁用看门狗。
- 获取平台设备路径,枚举当前存储设备上的分区。
- 搜索并解析引导配置文件(
boot.img、vendor_boot.img或UEFI分区)。 - 验证签名并加载内核、DTB、ramdisk到物理内存。
- 设置页表、为ExitBootServices做准备。
- 调用
gBS->ExitBootServices(ImageHandle, MapKey)并将控制权交给内核。
下面结构图展示了ABL入口在UEFI阶段中的位置及参数流向:
上图清晰展示了ABL入口在UEFI阶段的承接位置:经过SEC、PEI、DXE三层逐级初始化后,BDS阶段调用ABL入口,并传入 ImageHandle 和 SystemTable。右侧的说明框总结了与标准UEFI入口的核心区别,便于开发者快速定位差异点。
实现建议: 调试ABL入口时,可利用
SystemTable->ConOut->OutputString() 打印日志,但需注意此时启动服务尚未退出,不能依赖物理地址固定映射。推荐在入口点立即将串口重定向输出到 ConOut 上。