第3章 UEFI固件初始化与ABL环境准备

ABL(Android Bootloader)作为一个UEFI应用程序在TSL(临时系统加载)阶段被调用。它依赖的运行时环境——CPU模式、内存映射、芯片组寄存器以及服务表指针——全部由前序的UEFI阶段(SEC→PEI→DXE→BDS)建立。本章拆解这些初始化的核心环节,并说明ABL如何通过EFI_SYSTEM_TABLE获取所需服务。

UEFI启动阶段与ABL依赖关系
阶段主要行为ABL依赖程度
SEC (Security)设置临时栈、切换至保护模式间接 – 提供基础CPU模式
PEI (Pre-EFI Initialization)初始化内存控制器、CPU微码、Cache作为RAM高 – 内存拓扑必须正确
DXE (Driver Execution Environment)枚举PCI、初始化芯片组寄存器、安装UEFI协议高 – 服务表与Protocol可用
BDS (Boot Device Selection)选择启动设备并调用Boot Manager触发 – BDS加载ABL.efi
TSL (Temporary System Load)ABL在此阶段运行,使用Boot Services核心 – 所有内存/服务均就绪

3.1 CPU初始化

UEFI固件在SEC阶段将CPU从实模式切换至保护模式(或长模式),并建立初始GDT/IDT。PEI阶段会加载微码补丁、启用MTRR(内存类型范围寄存器)并设置页表。ABL启动时CPU已经处于64位长模式(AArch64或x64),OS装载器只需检查CR0、CR3即可确认状态。

; x64长模式检查(IA32_EFER.LME = 1)
mov ecx, 0xC0000080   ; MSR IA32_EFER
rdmsr
and eax, 0x100         ; LME bit
cmp eax, 0x100
jne .not_long_mode
ABL在启动早期会调用ArmDisableInterrupts()或CPU_ARCH_DISABLE_INTERRUPT宏以确保安全环境。

3.2 内存初始化

内存初始化分为两步:PEI阶段通过MCH(内存控制器中心)配置DIMM时序、DDR频率,并使用Cache-as-RAM (CAR)作为临时RAM;DXE阶段安装完整的全局内存映射。ABL依赖的服务表位于系统内存的高端区域,其物理地址在BDS阶段被写入UEFI_IMAGE_ENTRY_POINT参数。

// UEFI内存描述符结构 (EFI_MEMORY_DESCRIPTOR)
typedef struct {
    UINT32  Type;           // 0 = Reserved, 3 = BootServicesCode, ...
    UINT32  Pad;
    UINT64  PhysicalStart;
    UINT64  VirtualStart;
    UINT64  NumberOfPages;
    UINT64  Attribute;      // 缓存属性、预留位
} EFI_MEMORY_DESCRIPTOR;

ABL在BootServices->GetMemoryMap()中获取这个描述符列表,用于后续向Linux内核传递memmap参数。

内存类型EfiBootServicesData在ABL退出Boot Services后将被回收,ABL必须在此之前将关键数据(如FDT)拷贝到EfiRuntimeServicesData区域。

3.3 芯片组初始化

芯片组初始化包括南桥(PCH)、北桥(系统代理)以及各种I/O控制器(SATA、USB、PCIe Root Complex)。DXE驱动会执行以下典型操作:

  • PCI总线枚举 – 为每个设备分配总线号、内存/IO空间。
  • ACPI表构建 – 在内存中填写MADT、FADT、DBG2等表。
  • 寄存器锁定 – 例如SPI_BAR、GPIO_BASE、BDF_TABLE。

ABL直接使用的芯片组资源包括:

// 通过UEFI Protocol访问GPIO或I2C
EFI_STATUS Status;
EFI_GPIO_PROTOCOL *Gpio;

Status = gBS->LocateProtocol(&gEfiGpioProtocolGuid, NULL, (VOID**)&Gpio);
if (EFI_ERROR(Status)) {
    DEBUG((EFI_D_ERROR, "GPIO Protocol not found\n"));
    return Status;
}
芯片组初始化期间配置的LPC/eSPI基地址若被UEFI遗漏,ABL将无法访问调试串口。务必在BoardConfig中检查PLATFORM_PORT80寄存器。

3.4 ABL依赖的UEFI服务表

ABL入口函数EfiMain的第一个参数是ImageHandle,第二个参数是EFI_SYSTEM_TABLE指针。系统表是ABL与UEFI固件交互的唯一入口。

// 精简版系统表定义
typedef struct {
    EFI_TABLE_HEADER                Hdr;
    CHAR16                          *FirmwareVendor;
    UINT32                          FirmwareRevision;
    EFI_HANDLE                      ConsoleInHandle;
    EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *ConIn;
    EFI_HANDLE                      ConsoleOutHandle;
    EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
    EFI_HANDLE                      StandardErrorHandle;
    EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;
    EFI_RUNTIME_SERVICES            *RuntimeServices;
    EFI_BOOT_SERVICES               *BootServices;
    UINTN                           NumberOfTableEntries;
    EFI_CONFIGURATION_TABLE         *ConfigurationTable;
} EFI_SYSTEM_TABLE;

ABL主要使用BootServices(内存分配、协议定位、事件)和ConfigurationTable(获取ACPI表、设备树或SMBIOS)。

ABL常用UEFI服务
服务指针典型用途ABL阶段
gBS->AllocatePages为大内核分配连续内存加载内核前
gBS->LocateProtocol获取LoadFile2、PlatformInfo协议初始化时
gRT->GetVariable读取Boot Args变量配置解析
gST->ConfigurationTable获取设备树基址传递FDT
ABL在调用gBS->ExitBootServices()后不可再使用BootServices指针,必须提前将所有需要的数据复制到RuntimeServices内存中。
UEFI阶段与ABL环境准备流程 SEC CPU实/保护模式 PEI 内存初始化 + CAR DXE 芯片组+协议安装 BDS 选择ABL.efi TSL ABL运行 ABL环境准备关键产出 CPU模式 + GDT/IDT 内存映射 + Cache策略 芯片组寄存器 + PCI UEFI服务表

上图展示了UEFI初始化阶段的流水线以及ABL在TSL阶段所能依赖的核心环境。所有初始化工作在DXE结束时已经完成,ABL通过系统表直接存取这些资源。