1. ARMv8架构概述:AArch64与AArch32执行状态、异常等级(EL0-EL3)定义、安全世界与非安全世界

各位同学,咱们今天正式开讲ARMv8。说实话,ARMv8是个分水岭。在这之前,ARM处理器一直是32位的天下;从v8开始,64位计算被正式引入。但ARM没有像x86那样直接砍掉32位兼容,而是搞了一套双执行状态的设计——AArch64和AArch32。我个人觉得,这个设计既保留了生态兼容性,又给未来性能扩展留足了空间。

1.1 AArch64与AArch32:两种执行状态

ARMv8架构最核心的变化,就是引入了AArch64执行状态。你可以把它理解为处理器的“64位模式”。在这个模式下,寄存器是64位的,指令集是全新的A64,地址空间也扩展到了48位(物理地址甚至能到52位)。

而AArch32,说白了就是兼容老ARM代码的模式。它支持现有的ARMv7指令集(A32和T32),寄存器还是32位的。嗯,这里要注意:AArch32并不是简单的“向下兼容”,它其实是ARMv8硬件上重新实现的32位执行环境,性能并不差。

关键区别一览:

特性 AArch64 AArch32
寄存器宽度 64位通用寄存器(X0-X30) 32位通用寄存器(R0-R14)
指令集 A64(固定32位指令) A32(32位)/ T32(16/32位混合)
地址空间 最大48位虚拟地址 最大32位虚拟地址
异常等级 EL0-EL3完整支持 EL0-EL3(但PL0-PL2映射)
浮点/SIMD 默认支持(32×128位寄存器) 可选支持(VFP/NEON)

我在项目中遇到过一种情况:有些老驱动只编译了A32指令集,移植到新平台时发现跑不起来。后来查了半天,原来是启动时CPU默认进入AArch64,需要先切到AArch32才能执行这些代码。所以,两种状态之间的切换,是底层开发必须掌握的技能。

1.2 异常等级(EL0-EL3):ARMv8的权限分层

ARMv8引入了一个非常清晰的特权模型——异常等级(Exception Level)。你想想看,从EL0到EL3,数字越大,权限越高。这个设计比ARMv7的PL0-PL2要直观得多。

  • EL0:用户态。跑应用程序的地方,权限最低。不能直接操作硬件,不能配置MMU,连关中断都不行。
  • EL1:操作系统内核态。Linux内核、RTOS内核都跑在这里。可以配置MMU、处理中断、管理进程。
  • EL2:虚拟机监视器(Hypervisor)。虚拟化场景下,Host VM跑在这里。可以截获Guest OS的敏感操作。
  • EL3:安全监视器(Secure Monitor)。最高权限,负责安全世界与非安全世界的切换。ARM TrustZone的核心就在这里。

我的经验:调试异常等级相关的问题时,我建议你先确认当前CPU在哪个EL下运行。用MRS X0, CurrentEL指令读一下,返回值低两位就是EL编号。我曾经被一个EL2下的虚拟化中断搞了三天,最后发现是EL2的异常向量表配错了。

异常等级之间通过异常(Exception)来切换。比如,用户程序执行了SVC指令,就会触发一个同步异常,CPU从EL0跳到EL1。再比如,外设中断来了,如果当前在EL1,中断会直接由EL1的异常向量表处理。如果当前在EL0,则会先升到EL1再处理。

注意:异常等级只能升高,不能降低。降低等级必须通过异常返回(ERET)指令。而且ERET的目标等级由SPSR_ELx中的M字段决定。我曾经见过有人把SPSR写错了,ERET之后CPU直接跑飞——那个bug查得我头皮发麻。

1.3 安全世界与非安全世界:TrustZone的根基

ARMv8延续了ARMv7时代的TrustZone技术,但做了重要升级。在ARMv8中,安全世界(Secure World)和非安全世界(Non-secure World)是两个独立的执行环境,它们共享物理CPU,但拥有各自的内存、外设和异常向量表。

你想想看,这就像同一台物理机器上跑了两套系统:一套是“普通系统”(比如Android/Linux),另一套是“安全系统”(比如TEE OS)。普通系统永远无法访问安全系统的内存,即使它跑在EL3也不行——因为EL3本身就有安全和非安全两种状态。

具体来说,安全世界和非安全世界的划分是这样的:

  • 非安全世界(Normal World):运行普通操作系统和应用程序。所有EL0-EL2都在非安全世界。
  • 安全世界(Secure World):运行安全操作系统(如OP-TEE、Trusty)。安全世界有自己的EL0-EL3,但通常只用到EL0和EL1。
  • EL3:唯一横跨两个世界的异常等级。安全监视器(Secure Monitor)运行在这里,负责两个世界之间的切换。

世界切换流程(简化版):

  1. 非安全世界的EL1执行SMC指令(安全监视器调用)。
  2. CPU陷入EL3,安全监视器保存非安全世界上下文。
  3. 安全监视器切换到安全世界,恢复安全世界上下文。
  4. 安全世界的EL1开始执行安全服务。
  5. 安全服务完成后,执行SMC返回EL3。
  6. 安全监视器恢复非安全世界上下文,ERET回到非安全世界。

我在项目中遇到过一个问题:安全世界的定时器中断没有正确路由到EL3,导致安全服务超时。后来发现是GIC(通用中断控制器)的配置问题——安全中断必须配置为Group 0,并且路由到EL3。这个坑,我建议你们提前记住。

1.4 整体架构关系图

下面这张图,是我自己画的一个ARMv8架构概览。它把执行状态、异常等级、安全世界的关系串在了一起。你仔细看看,应该能对ARMv8的整体结构有个直观印象。

ARMv8 架构概览:执行状态 · 异常等级 · 安全世界 AArch64 执行状态 AArch32 执行状态 EL0 - 用户态 (App) 非安全世界 / 安全世界 EL1 - 操作系统内核 Linux / RTOS / TEE OS EL2 - 虚拟机监视器 Hypervisor (仅非安全世界) EL3 - 安全监视器 Secure Monitor (唯一跨世界) EL0 - 用户态 (App) 非安全世界 / 安全世界 EL1 - 操作系统内核 Linux / RTOS / TEE OS EL2 - 虚拟机监视器 Hypervisor (仅非安全世界) EL3 - 安全监视器 Secure Monitor (唯一跨世界) 安全世界 非安全世界 安全世界 非安全世界 SMC 切换 异常 异常 异常 说明:AArch64 和 AArch32 共享相同的异常等级和安全世界模型,但寄存器宽度和指令集不同

1.5 本章小结

好了,咱们把ARMv8架构的三大核心概念捋了一遍:

  • AArch64 vs AArch32:64位和32位两种执行状态,寄存器宽度、指令集、地址空间都不一样。切换时要小心上下文保存。
  • EL0-EL3:四个异常等级,从用户态到安全监视器,权限逐级递增。异常只能升不能降,ERET是唯一的降级手段。
  • 安全世界 vs 非安全世界:TrustZone的两个世界,通过EL3的SMC指令切换。安全世界的内存对外部完全隔离。

这些概念,是后续所有异常处理、调试接口、故障定位的基础。你如果现在觉得有点绕,没关系——后面每一章都会反复用到这些知识。我个人建议你把这张架构图存下来,调试的时候对照着看,会清晰很多。