2. ARM64架构基础
在进入 ARM64 早期启动代码之前,必须先建立对 AArch64 架构基本模型的清晰认识。启动流程中的每一条指令、每一次异常向量跳转、每一个系统寄存器写入,都建立在本节所述的执行状态、异常等级、寄存器体系与内存模型之上。理解这些概念,是读懂 _start、el3_entry、el2_entry 等入口代码的前提。
一、AArch64 执行状态与 AArch32 的关系
ARMv8-A 架构引入了 AArch64 执行状态,它使用 64 位宽的通用寄存器与固定的 32 位指令编码。与之并存的 AArch32 状态则兼容早期 ARMv7 的 32 位执行模型。两者并非简单的“模式切换”,而是两套独立的执行环境:寄存器宽度、异常模型、系统寄存器命名空间都不相同。
关键点在于:执行状态由异常等级决定。同一个异常等级下,处理器可以运行在 AArch64 或 AArch32,但一旦进入更高异常等级,执行状态可能被强制切换。例如从 EL0 的 AArch32 应用触发异常进入 EL1 时,EL1 可以配置为 AArch64,此时处理器自动切换到 64 位执行状态。这种“低 EL 可为 32 位、高 EL 为 64 位”的混合模型,是 ARM64 启动代码必须处理的第一类状态问题。
二、异常等级 EL0–EL3 的层级模型
ARM64 定义了四个异常等级,数值越大权限越高:
- EL0:用户态,运行应用程序,权限最低,不能直接访问系统寄存器。
- EL1:内核态,通常运行 Linux 内核或 RTOS,负责内存管理、中断处理。
- EL2:虚拟化层,运行 Hypervisor,管理虚拟机与 Stage-2 地址翻译。
- EL3:安全监控层,运行 Secure Monitor,负责安全世界与非安全世界的切换。
异常只能从低 EL 向高 EL 或同 EL 触发,不能主动降级;降级必须通过 ERET 指令配合 SPSR_ELx 与 ELR_ELx 完成。启动代码通常从 EL3 开始,依次初始化并逐级下降,最终进入 EL1 运行操作系统。
三、通用寄存器与特殊寄存器
AArch64 提供 31 个 64 位通用寄存器 X0–X30,其低 32 位可通过 W0–W30 访问。写入 W 寄存器会自动清零对应 X 寄存器的高 32 位。此外还有零寄存器 XZR/WZR,读取恒为 0,写入被丢弃。
几个具有约定用途的寄存器在启动代码中频繁出现:
| 寄存器 | 约定用途 | 启动阶段典型场景 |
|---|---|---|
| X0–X7 | 参数传递 / 返回值 | 传递设备树地址、启动参数 |
| X8 | 间接结果寄存器 | 系统调用号传递 |
| X9–X15 | 临时寄存器 | 启动代码临时保存中间值 |
| X16/X17 | IP0/IP1,链接器暂存 | PLT 跳转、长跳转 |
| X18 | 平台保留寄存器 | 部分平台用于指向 per-CPU 数据 |
| X29 | 帧指针 FP | 函数调用栈回溯 |
| X30 | 链接寄存器 LR | 保存返回地址 |
| SP_EL0–SP_EL3 | 各异常等级独立栈指针 | 切换 EL 时自动切换栈 |
注意:AArch64 中 PC 不再是可直接读写的通用寄存器,异常返回地址保存在 ELR_ELx 中,通过 ERET 恢复执行。
四、系统寄存器与内存模型
系统寄存器通过 MRS(读)和 MSR(写)访问,命名格式为 寄存器名_ELx。启动代码中最关键的系统寄存器包括:
SCTLR_ELx:系统控制寄存器,控制 MMU、Cache、对齐检查等全局开关。TCR_ELx:翻译控制寄存器,定义页表基址范围、颗粒度、IPA 范围。TTBR0_ELx / TTBR1_ELx:页表基址寄存器,分别对应用户空间与内核空间。VBAR_ELx:异常向量基址寄存器,决定异常入口地址。DAIF:中断屏蔽位,控制 IRQ、FIQ、SError、Debug 异常。SPSR_ELx / ELR_ELx:异常返回时的状态与地址保存。
ARM64 采用弱内存模型,处理器和编译器都可能对内存访问重排序。启动阶段在使能 MMU、写入页表、配置设备寄存器时,必须使用屏障指令保证顺序:
dsb sy // 数据同步屏障,等待所有内存访问完成
isb // 指令同步屏障,刷新流水线
dmb sy // 数据内存屏障,保证访问顺序
tlbi vmalle1 // 失效 EL1 阶段所有 TLB 条目
dsb 与 isb,导致页表尚未对处理器可见就进行地址翻译,引发不可预期的取指异常。正确顺序是:写页表基址 → dsb → tlbi → dsb → isb → 使能 MMU。
五、启动场景中的综合运用
以典型的 BL31 启动为例,代码从 EL3 入口开始,首先读取 CurrentEL 确认当前异常等级,随后设置 VBAR_EL3 指向异常向量表,配置 SCTLR_EL3 关闭 MMU 与 Cache,再通过 SCR_EL3 配置安全世界路由,最后构造 SPSR_EL2 与 ELR_EL2,执行 ERET 下降至 EL2。整个过程体现了异常等级、系统寄存器与内存屏障三者的协同。