2. ATF基础概念
异常模型,说白了就是 CPU 在遇到「意外情况」时怎么切换工作模式。ARMv8-A 的异常模型设计得相当精巧,我当年刚接触时也被绕晕过。但搞懂它,你才能真正理解 ATF 为什么能作为安全世界的「看门人」。
异常级别 EL0-EL3:四个特权世界
ARMv8-A 把 CPU 的执行状态分成了四个等级,叫 Exception Level,简称 EL。数字越大,权限越高。
| 异常级别 | 典型用途 | 运行环境 |
|---|---|---|
| EL0 | 用户态应用程序 | 非安全/安全 |
| EL1 | 操作系统内核 | 非安全/安全 |
| EL2 | 虚拟机监视器 (Hypervisor) | 非安全 |
| EL3 | 安全监视器 (Secure Monitor) | 安全 |
EL0 是最底层,跑你的 App。EL1 跑操作系统,比如 Linux 内核。EL2 是虚拟化层,跑 KVM 或 Xen。EL3 是最高权限层,ATF 就待在这里。
关键点:EL3 是唯一一个永远处于安全世界的异常级别。它负责管理非安全世界(EL1/EL0)和安全世界(S-EL1/S-EL0)之间的切换。说白了,EL3 就是两个世界的「海关」。我在项目中见过有人把安全配置搞错,结果 EL3 的代码被非安全世界篡改——那场面,直接重启都救不回来。
同步异常与异步异常
ARMv8-A 把异常分成两大类:同步异常和异步异常。区别很简单——同步异常是 CPU 执行指令时自己搞出来的,异步异常是外部事件触发的。
同步异常
同步异常和当前执行的指令直接相关。比如你访问了一个不存在的内存地址,或者执行了一条未定义的指令。CPU 会立即停下来,跳转到异常向量表去处理。
- SVC (Supervisor Call):用户态程序主动调用内核服务。比如 Linux 里的系统调用。
- HVC (Hypervisor Call):虚拟机里的 Guest OS 调用 Hypervisor。
- SMC (Secure Monitor Call):非安全世界调用安全世界。ATF 就是靠这个接口被调用的。
- Data Abort / Prefetch Abort:内存访问失败或指令预取失败。
- Undefined Instruction:CPU 不认识这条指令。
我个人的习惯:调试同步异常时,先看 ESR (Exception Syndrome Register)。它里面记录了异常的具体原因,比如是 Alignment fault 还是 Translation fault。省得你瞎猜。
异步异常
异步异常和当前指令没有直接关系。它们是外部事件,比如中断或系统错误。
- IRQ (Interrupt Request):普通中断。外设发来的。
- FIQ (Fast Interrupt Request):快速中断。优先级更高,延迟更低。
- SError (System Error):系统错误。比如内存 ECC 校验失败、总线超时。
你想想看,为什么 ARM 要区分 IRQ 和 FIQ?我当年做安全启动时,就把 FIQ 留给了安全世界的定时器,确保安全监控任务不会被普通中断打断。嗯,这个设计在攻击场景下特别有用——攻击者想用大量 IRQ 淹没 CPU,但 FIQ 依然能准时响应。
异常路由机制:谁来决定异常去哪?
异常发生后,CPU 怎么知道该跳到哪个 EL 去处理?这就靠异常路由机制了。ARMv8-A 提供了一套寄存器来控制路由策略。
核心寄存器
- SCR_EL3 (Secure Configuration Register):EL3 的配置寄存器。它控制着 IRQ、FIQ、SError 和 SMC 是否路由到 EL3。
- HCR_EL2 (Hypervisor Configuration Register):EL2 的配置寄存器。控制非安全世界的异常路由。
- RVBAR_EL3 (Reset Vector Base Address Register):复位向量基地址。CPU 上电后第一条指令从这取。
举个例子,假设你希望所有 FIQ 都直接交给 EL3 处理,而不是让 EL1 或 EL2 先碰。你就在 SCR_EL3 里设置 FIQ 位为 1。这样,任何 EL 下产生的 FIQ 都会直接路由到 EL3。
我曾经踩过的坑:在调试 Secure Monitor 时,忘了配置 SCR_EL3 的 SMC 路由位。结果非安全世界发 SMC 调用,CPU 直接 Undefined Exception 了。查了半天才发现是路由没开。记住:SMC 默认不会路由到 EL3,你得手动配。
异常路由的典型场景
- 非安全世界的 IRQ:通常路由到 EL1(Linux 内核)或 EL2(Hypervisor)。
- 安全世界的 IRQ:路由到 S-EL1(安全 OS)或 EL3(ATF)。
- FIQ:我建议全部路由到 EL3,由 ATF 统一分发。这样可以避免安全中断被非安全世界干扰。
- SMC:必须路由到 EL3。这是非安全世界调用安全世界的唯一通道。
- SError:通常路由到 EL3,由 ATF 记录错误并决定是否恢复。
异常向量表:CPU 的跳转地图
每个异常级别都有自己的异常向量表。向量表是一个 2KB 对齐的内存区域,里面放了 16 个入口,每个入口对应一种异常类型。
ARMv8-A 的向量表结构如下:
// 异常向量表布局(每个入口 128 字节)
// 偏移量 0x000: 同步异常 (Current EL with SP0)
// 偏移量 0x080: IRQ (Current EL with SP0)
// 偏移量 0x100: FIQ (Current EL with SP0)
// 偏移量 0x180: SError (Current EL with SP0)
// 偏移量 0x200: 同步异常 (Current EL with SPx)
// 偏移量 0x280: IRQ (Current EL with SPx)
// 偏移量 0x300: FIQ (Current EL with SPx)
// 偏移量 0x380: SError (Current EL with SPx)
// 偏移量 0x400: 同步异常 (Lower EL using AArch64)
// 偏移量 0x480: IRQ (Lower EL using AArch64)
// 偏移量 0x500: FIQ (Lower EL using AArch64)
// 偏移量 0x580: SError (Lower EL using AArch64)
// 偏移量 0x600: 同步异常 (Lower EL using AArch32)
// 偏移量 0x680: IRQ (Lower EL using AArch32)
// 偏移量 0x700: FIQ (Lower EL using AArch32)
// 偏移量 0x780: SError (Lower EL using AArch32)
ATF 在启动时会为 EL3 设置向量表,地址放在 VBAR_EL3 寄存器里。每个入口只有 128 字节,通常放一个跳转指令,跳到真正的处理函数。
避坑指南:向量表必须 2KB 对齐。我曾经因为链接脚本没对齐,导致异常跳到了错误的地方。调试器一看 PC 值,完全不在向量表范围内。嗯,那次排查花了我一整天。
异常处理流程:从触发到返回
一个完整的异常处理流程是这样的:
- 异常触发:CPU 检测到异常条件(比如 SMC 指令执行)。
- 保存上下文:CPU 自动保存 PSTATE 到 SPSR_ELx,保存返回地址到 ELR_ELx。
- 切换异常级别:CPU 根据路由配置,切换到目标 EL。
- 跳转向量表:CPU 从 VBAR_ELx + 偏移量 取出入口地址,开始执行处理代码。
- 处理异常:软件保存更多寄存器(比如通用寄存器),然后执行具体处理逻辑。
- 异常返回:执行 ERET 指令,CPU 从 SPSR_ELx 恢复 PSTATE,从 ELR_ELx 恢复返回地址,继续执行。
在 ATF 里,SMC 异常的处理流程特别重要。非安全世界发 SMC,CPU 自动跳到 EL3 的同步异常入口。ATF 解析 SMC 的功能 ID,然后调用对应的处理函数。处理完后再 ERET 回去。
核心要点:异常处理过程中,CPU 会自动屏蔽同级别或更低级别的中断。比如在 EL3 处理 SMC 时,除非你手动开中断,否则 IRQ 和 FIQ 都不会被响应。这是为了防止处理过程中被干扰。
SVG:ARMv8-A 异常模型全景图
下面这张图展示了异常级别、异常类型和路由关系。我建议你把它存下来,调试时对照着看。
实际项目中的异常配置建议
基于我多年的嵌入式安全经验,给你几条配置建议:
- FIQ 全部路由到 EL3:让 ATF 统一管理所有快速中断。安全世界的定时器、加密引擎完成中断都走 FIQ。
- SMC 必须路由到 EL3:这是安全调用的唯一入口。别让其他 EL 拦截 SMC。
- SError 路由到 EL3:系统错误通常不可恢复,让 ATF 记录日志并决定是否重启。
- IRQ 按需分配:非安全中断给 EL1/EL2,安全中断给 S-EL1 或 EL3。
重要提醒:异常路由配置必须在 EL3 完成。一旦配置错误,非安全世界可能劫持安全中断。我见过一个案例,攻击者利用配置错误的 FIQ 路由,伪造安全中断来触发 EL3 的漏洞。嗯,从那以后我每次做安全启动都会 triple-check SCR_EL3 的值。
ARMv8-A 的异常模型,说白了就是一套「谁有权处理什么事」的规则。EL3 是最高裁判,EL2 是虚拟化裁判,EL1 是操作系统裁判,EL0 只能乖乖听话。搞懂这套规则,你才能理解 ATF 为什么能成为安全世界的基石。