2. ARM64 异常模型:异常等级、同步/异步异常与异常向量表
ARM64 架构的异常模型是理解缺页中断处理流程的基石。缺页中断本质上是一种由 MMU 触发的同步异常,其路由、处理入口和返回机制完全由异常模型定义。本节将系统梳理 ARM64 异常模型的三个核心维度:异常等级(Exception Level)、异常分类(同步/异步)以及异常向量表(Vector Table)。
2.1 异常等级(Exception Levels: EL0-EL3)
ARM64 定义了四个异常等级,数字越大特权越高。每个等级拥有独立的虚拟地址空间、页表基址寄存器(TTBR0_ELn/TTBR1_ELn)和系统控制寄存器。
| 异常等级 | 名称 | 运行模式 | 典型用途 | 页表选择 |
|---|---|---|---|---|
| EL0 | 用户态 | 非特权 | 应用程序、用户库 | TTBR0_EL0 |
| EL1 | 内核态 | 特权 | 操作系统内核、内核模块 | TTBR0_EL1 / TTBR1_EL1 |
| EL2 | 虚拟机监视器 | Hypervisor | KVM、虚拟化扩展 | TTBR0_EL2 |
| EL3 | 安全监视器 | Secure Monitor | 安全启动、TrustZone 切换 | TTBR0_EL3 |
异常等级之间的切换通过异常产生(Exception Generation)和异常返回(ERET)实现。当低等级代码触发异常时,CPU 自动提升到目标异常等级,并保存返回地址到 ELR_ELx 寄存器,保存处理器状态到 SPSR_ELx 寄存器。
ELR_ELx(Exception Link Register):保存异常返回地址。SPSR_ELx(Saved Program Status Register):保存异常发生时的 PSTATE。ESR_ELx(Exception Syndrome Register):记录异常原因和附加信息。FAR_ELx(Fault Address Register):记录引发异常的虚拟地址(仅数据/指令异常时有效)。
2.2 同步异常与异步异常
ARM64 将异常分为两大类:同步异常(Synchronous Exception)和异步异常(Asynchronous Exception)。缺页中断属于典型的同步异常。
| 特性 | 同步异常 | 异步异常 |
|---|---|---|
| 触发方式 | 由当前指令直接引发 | 由外部事件或系统状态引发 |
| 返回地址 | 指向触发异常的指令(可重试) | 指向下一条未执行的指令 |
| 可重复性 | 相同条件下可重复触发 | 不可预测,与指令流无关 |
| 典型类型 | 缺页异常、对齐异常、SVC/HVC/SMC、断点 | IRQ(普通中断)、FIQ(快速中断)、SError(系统错误) |
| ESR_ELx 有效性 | 有效,记录异常原因 | IRQ/FIQ 不记录 ESR;SError 记录 |
| FAR_ELx 有效性 | 数据/指令异常时有效 | 通常无效 |
ELR_ELx 指向触发异常的指令,内核处理完成后必须重新执行该指令。
同步异常根据异常原因(Exception Class, EC)字段进一步细分。ESR_ELx 寄存器的 [31:26] 位记录 EC 值,内核通过 EC 值判断异常类型。
| EC 值 | 异常类型 | 说明 |
|---|---|---|
| 0b000000 | Unknown | 未知原因 |
| 0b000001 | Trapped WF* | WFI/WFE 指令捕获 |
| 0b010001 | SVC (Supervisor Call) | 系统调用(EL0→EL1) |
| 0b100000 | Instruction Abort (MMU) | 取指阶段缺页 |
| 0b100001 | Instruction Abort (External) | 外部中止 |
| 0b100100 | Data Abort (MMU) | 数据访问缺页(缺页中断核心类型) |
| 0b100101 | Data Abort (External) | 外部数据中止 |
| 0b101100 | SError | 系统错误中断 |
2.3 异常向量表(Vector Table)
ARM64 的异常向量表是异常处理的入口映射表。每个异常等级(EL1/EL2/EL3)拥有独立的向量表基址寄存器 VBAR_ELx(Vector Base Address Register)。向量表固定包含 16 个入口,每个入口占用 128 字节(0x80),总大小 2048 字节(0x800)。
向量表的 16 个入口由四个偏移组(Offset Group)和四个异常类型(Exception Type)组合决定:
| 偏移组 | 偏移地址 | 触发条件 |
|---|---|---|
| Sync | +0x000 | 同步异常(Synchronous) |
| IRQ | +0x080 | 普通中断(IRQ) |
| FIQ | +0x100 | 快速中断(FIQ) |
| SError | +0x180 | 系统错误(SError) |
每个偏移组根据异常发生时的当前异常等级和栈指针选择(SPsel)进一步分为四个子入口:
| 子入口 | 偏移地址 | 触发条件 |
|---|---|---|
| EL0 Sync | +0x000 | 异常来自 EL0(用户态) |
| EL1 Sync (SP_EL0) | +0x200 | 异常来自 EL1,使用 SP_EL0 |
| EL1 Sync (SP_EL1) | +0x400 | 异常来自 EL1,使用 SP_EL1 |
| EL0 Async (AArch32) | +0x600 | 异常来自 EL0,AArch32 模式 |
arch/arm64/kernel/entry.S 中定义 vectors 标签,并通过 msr vbar_el1, x0 设置向量表基址。
2.4 异常处理核心流程
当 ARM64 CPU 检测到异常时,硬件自动执行以下序列:
- 确定目标异常等级:根据异常类型和当前配置,决定路由到 EL1/EL2/EL3。
- 保存上下文:将返回地址写入
ELR_ELx,将 PSTATE 写入SPSR_ELx。 - 记录异常信息:将异常原因写入
ESR_ELx,将故障虚拟地址写入FAR_ELx(仅数据/指令异常)。 - 更新 PSTATE:设置 PSTATE 为目标异常等级,禁用 IRQ/FIQ(可选),切换栈指针。
- 跳转向量表:根据
VBAR_ELx+ 偏移量,跳转到对应的异常处理入口。 - 软件处理:内核在入口处保存完整寄存器上下文,调用 C 语言处理函数。
- 异常返回:执行
ERET指令,从ELR_ELx恢复 PC,从SPSR_ELx恢复 PSTATE。