1、启动概述:ARM64系统启动的宏观视角,UEFI与ATF的角色定位

各位同学,咱们今天聊聊ARM64系统的启动。说实话,这个主题我琢磨了十几年,从早期的裸机启动到现在的UEFI+ATF组合,变化真的很大。你想想看,一个手机按下电源键到出现Logo,背后经历了多少环节?我刚开始做嵌入式时,总觉得启动就是“上电→跑代码”这么简单,后来踩了不少坑才明白——这里面的门道,深着呢。

1.1 从按下电源键说起

ARM64系统的启动,说白了就是一场“接力赛”。从硬件复位到操作系统跑起来,中间要经过好几个阶段。每个阶段都有自己的任务,就像接力赛中的运动员,一棒接一棒。

我个人习惯把启动过程分成三个大阶段:

  • 硬件初始化阶段:CPU复位、内存初始化、时钟配置
  • 固件引导阶段:UEFI或Bootloader加载、ATF安全监控初始化
  • 操作系统启动阶段:内核解压、设备驱动加载、用户空间初始化

嗯,这里要注意:ARM64和x86的启动方式完全不同。x86有BIOS/UEFI作为标准固件接口,而ARM64早期是百花齐放——各家芯片厂都有自己的启动方案。直到UEFI和ATF(ARM Trusted Firmware)逐渐普及,才形成了相对统一的规范。

1.2 UEFI在ARM64启动中的角色

UEFI(统一可扩展固件接口)在ARM64上扮演什么角色?我打个比方:它就像系统启动时的“物业公司”。负责把硬件环境准备好,然后告诉操作系统:“房子已经打扫干净了,你直接入住吧。”

具体来说,UEFI在ARM64启动中做这几件事:

  1. 硬件初始化:配置内存控制器、初始化中断控制器、设置系统时钟
  2. 设备发现:通过ACPI或Device Tree描述硬件拓扑
  3. 启动管理器:选择从哪个设备启动(硬盘、网络、USB等)
  4. 运行时服务:提供操作系统运行期间可以调用的固件接口

关键点:UEFI在ARM64上运行于EL2(异常级别2),也就是虚拟机监视器级别。这意味着它拥有比操作系统更高的权限,可以管理内存映射和中断路由。

我在项目中遇到过一个问题:某款ARM64服务器在UEFI阶段总是卡在内存初始化。查了三天,最后发现是内存条插槽接触不良。嗯,硬件问题有时候就是这么坑。

1.3 ATF:安全世界的守护者

ATF(ARM Trusted Firmware)是ARM官方提供的安全固件参考实现。它的核心任务是管理ARM架构中的安全世界(Secure World)和普通世界(Normal World)之间的切换。

你可能会问:为什么要分安全世界和普通世界?

举个例子:手机上的指纹识别、支付验证这些敏感操作,如果和普通App运行在同一个环境,很容易被攻击。ATF通过ARM TrustZone技术,在硬件层面隔离出安全世界,专门处理这些敏感任务。

ATF在启动流程中的角色:

  • BL1:启动ROM代码,负责最基本的硬件初始化
  • BL2:可信启动加载器,验证后续固件的签名
  • BL31:运行时固件,管理安全监控调用(SMC)
  • BL32:可选的安全操作系统(如OP-TEE)
  • BL33:非安全世界的引导加载器(通常是UEFI)

个人经验:我曾经在调试ATF时,发现BL31的SMC处理函数有个bug,导致安全世界调用返回时栈指针错乱。修这个bug花了我整整两天,最后发现是编译器优化把关键变量优化掉了。从那以后,我写ATF代码都会加上volatile关键字。

1.4 UEFI与ATF的协作关系

UEFI和ATF不是各自为政的,它们之间有明确的接口和协作机制。我画了一张图来展示它们的关系:

ARM64系统启动:UEFI与ATF协作架构 EL3:ATF BL31(安全监控器) EL2:UEFI / Hypervisor EL1:操作系统内核(Linux / Windows) EL0:用户空间应用程序 普通世界(Normal World) 安全世界(Secure World) SMC调用 返回结果 启动顺序:BL1 → BL2 → BL31 → BL32(可选)→ BL33(UEFI)→ 操作系统

从这张图可以看出:

  • ATF运行在最高权限的EL3,负责安全监控
  • UEFI运行在EL2,负责硬件初始化和启动管理
  • 操作系统运行在EL1,用户程序在EL0
  • UEFI通过SMC指令调用ATF的服务

避坑指南:我曾经在移植UEFI到新平台时,忽略了ATF的SMC处理函数实现。结果UEFI一调用SMC就死机。后来发现是ATF的BL31没有正确注册SMC处理函数。记住:UEFI和ATF的接口必须严格匹配,否则系统连Logo都看不到。

1.5 启动流程的时间线

咱们用表格来梳理一下整个启动流程的时间线:

阶段 执行者 异常级别 主要任务 典型耗时
BL1 BootROM EL3 最小硬件初始化,加载BL2 10-50ms
BL2 可信固件 EL3 验证签名,加载BL31/BL32 50-200ms
BL31 ATF运行时 EL3 初始化安全监控,等待SMC 10-30ms
BL33 UEFI EL2 硬件初始化,加载启动管理器 500ms-2s
OS加载 操作系统 EL1 内核解压,驱动初始化 1-5s

嗯,这里要注意:实际耗时取决于硬件性能和固件优化程度。我在某款服务器上见过UEFI阶段耗时超过10秒的,原因是ACPI表生成逻辑太复杂。后来优化了一下,降到了1.5秒。

1.6 为什么需要UEFI+ATF的组合?

你可能会问:以前ARM系统不也用U-Boot启动吗?为什么现在要搞这么复杂?

原因有三:

  1. 安全需求:现代设备需要可信启动、安全支付、DRM保护,ATF提供了硬件级的安全隔离
  2. 标准化:UEFI提供了统一的启动接口,让操作系统可以跨平台运行
  3. 可维护性:UEFI支持驱动模型,可以在固件阶段加载各种设备驱动

我的看法:UEFI+ATF的组合虽然增加了启动复杂度,但换来了更好的安全性和可移植性。对于消费电子和服务器市场来说,这是值得的。但对于资源受限的嵌入式设备,U-Boot仍然是更轻量的选择。

1.7 本章小结

咱们这一章从宏观角度梳理了ARM64系统的启动流程。核心要点:

  • 启动是分阶段进行的,每个阶段有明确的职责
  • UEFI负责硬件初始化和启动管理,运行在EL2
  • ATF负责安全监控和世界切换,运行在EL3
  • 两者通过SMC指令协作
  • 启动顺序:BL1 → BL2 → BL31 → BL33(UEFI) → OS

下一章咱们会深入UEFI的启动流程,看看它具体是怎么初始化硬件、加载驱动的。到时候我会分享一些实际调试中的经验,包括怎么用JTAG跟踪UEFI的执行流程。

课后思考:如果你要在新平台上移植UEFI和ATF,你会先从哪里入手?我个人建议先从ATF的BL2开始,因为它是整个信任链的根。搞定了BL2,后面的工作会顺利很多。

可可精品资料库