1. ARMv8 架构概述:执行状态、异常等级与寄存器模型
各位同学,咱们今天正式开讲《ARMv8 内存管理与 MMU 配置调试实战》的第一章。说实话,ARMv8 刚出来那会儿,我也觉得它挺复杂的——尤其是从 ARMv7 跳过来的时候。但后来我发现,只要把几个核心概念理清楚,后面的 MMU 配置其实水到渠成。
这一章咱们就聊聊 ARMv8 的三大基石:执行状态、异常等级和寄存器模型。别小看这些基础,我见过不少工程师在调试时翻车,就是因为没搞懂 EL0 和 EL1 的区别。
1.1 AArch64 与 AArch32:两种执行状态
ARMv8 最让我眼前一亮的设计,就是它同时支持两种执行状态——AArch64 和 AArch32。说白了,就是一颗芯片能跑 64 位和 32 位两种模式。
| 特性 | AArch64 | AArch32 |
|---|---|---|
| 寄存器宽度 | 64 位通用寄存器 | 32 位通用寄存器 |
| 指令集 | A64(固定 32 位指令) | A32/T32(ARM/Thumb) |
| 异常等级 | EL0-EL3,共 4 级 | PL0-PL3,共 4 级 |
| 虚拟地址宽度 | 最大 48 位(可选 52 位) | 最大 32 位 |
| MMU 页表格式 | L0-L3 四级页表 | 一级/二级页表 |
我个人习惯在项目初期就确定好执行状态。如果你跑的是 Linux,那基本就是 AArch64 没跑了。但如果你要做兼容老系统的 bootloader,AArch32 可能更合适。
1.2 异常等级(EL0-EL3):权限的分层设计
ARMv8 把运行权限分成了 4 个等级,从 EL0 到 EL3。数字越大,权限越高。你想想看,这就像一栋楼的楼层——EL0 是地下室,EL3 是顶楼总裁办公室。
- EL0:用户态,跑应用程序。权限最低,不能直接操作硬件。
- EL1:操作系统内核态。Linux 内核就跑在这儿,能配 MMU、中断控制器。
- EL2:虚拟化层。Hypervisor 的领地,管理多个虚拟机。
- EL3:安全监控层。Secure Monitor 的专属,负责安全世界和普通世界的切换。
我曾经在一个项目里踩过坑——在 EL1 尝试写一个 EL2 才允许访问的寄存器,结果直接触发 Undefined Instruction 异常。嗯,从那以后我写代码前都会先查一下当前异常等级。
1.3 寄存器模型:从 X0 到 X30 再到系统寄存器
AArch64 的寄存器模型比 ARMv7 清爽多了。通用寄存器从 X0 到 X30,一共 31 个 64 位寄存器。X30 是链接寄存器(LR),存返回地址。
还有几个特殊的:
- SP:栈指针,每个异常等级有自己的 SP_ELx。
- PC:程序计数器,不能直接写,只能通过分支指令修改。
- XZR:零寄存器,读出来永远是 0,写进去被忽略。
系统寄存器这块,我建议你重点关注几个:
| 寄存器 | 全称 | 作用 |
|---|---|---|
| SCTLR_EL1 | System Control Register | 控制 MMU、Cache、对齐检查等 |
| TCR_EL1 | Translation Control Register | 配置页表格式、地址宽度、内存属性 |
| TTBR0_EL1 / TTBR1_EL1 | Translation Table Base Register | 存放页表基地址 |
| MAIR_EL1 | Memory Attribute Indirection Register | 定义内存属性(Cache 策略、共享属性) |
MSR 和 MRS 指令读写。别想着用 LDR/STR 去碰它们——我见过有人这么干,结果就是 Undefined 异常。
1.4 知识结构图:ARMv8 架构核心脉络
下面这张图是我自己整理的,把执行状态、异常等级和寄存器模型串在了一起。你把它记在脑子里,后面学 MMU 会轻松很多。
1.5 实战中的一点体会
说了这么多理论,我分享一个实际案例吧。之前做一款车规级芯片的 bootloader,需要在 EL3 初始化安全世界,然后切到 EL1 启动 Linux。当时我犯了个低级错误——在 EL3 配了 MMU,但忘了切页表,结果 Linux 一启动就挂了。
后来怎么解决的?我在 EL3 的退出代码里加了一行 MSR TTBR0_EL1, x0,把正确的页表基地址传给 EL1。嗯,就是这么一行代码,折腾了我两天。