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
关键点:缺页中断发生时,CPU 通常从 EL0(用户态)或 EL1(内核态)陷入到 EL1 的异常向量表。如果系统启用了虚拟化(KVM),缺页可能先被 EL2 捕获并处理。

异常等级之间的切换通过异常产生(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):记录引发异常的虚拟地址(仅数据/指令异常时有效)。
ARM64 异常等级分层架构 EL3 — 安全监视器 (Secure Monitor) 安全启动、TrustZone 切换、电源管理 EL2 — 虚拟机监视器 (Hypervisor) KVM 虚拟化、Stage-2 地址转换、虚拟机间隔离 EL1 — 内核态 (OS Kernel) Linux 内核、系统调用处理、缺页中断处理、设备驱动 EL0 — 用户态 (Userspace) 应用程序、用户库、无特权指令执行 异常产生时,CPU 自动提升到目标异常等级;ERET 指令返回时降低等级

2.2 同步异常与异步异常

ARM64 将异常分为两大类:同步异常(Synchronous Exception)和异步异常(Asynchronous Exception)。缺页中断属于典型的同步异常。

特性 同步异常 异步异常
触发方式 由当前指令直接引发 由外部事件或系统状态引发
返回地址 指向触发异常的指令(可重试) 指向下一条未执行的指令
可重复性 相同条件下可重复触发 不可预测,与指令流无关
典型类型 缺页异常、对齐异常、SVC/HVC/SMC、断点 IRQ(普通中断)、FIQ(快速中断)、SError(系统错误)
ESR_ELx 有效性 有效,记录异常原因 IRQ/FIQ 不记录 ESR;SError 记录
FAR_ELx 有效性 数据/指令异常时有效 通常无效
缺页中断定位:缺页中断(Data Abort / Instruction Abort)是同步异常。当 MMU 无法完成地址转换时,CPU 在指令执行阶段(而非取指阶段)触发 Data Abort,或在取指阶段触发 Instruction Abort。异常返回地址 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 系统错误中断
同步异常 vs 异步异常处理流程对比 同步异常(缺页中断) 指令执行触发 Data Abort CPU 保存 ELR = 当前指令地址 内核处理完成后 ERET 返回 重新执行触发异常的指令 异步异常(IRQ/FIQ) 外部中断信号到达 CPU CPU 保存 ELR = 下一条指令地址 中断处理完成后 ERET 返回 继续执行下一条指令 同步异常:ELR 指向触发指令 → 处理完成后必须重试;异步异常:ELR 指向下一条指令 → 处理完成后继续

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 模式
缺页中断向量入口:当用户态程序触发缺页中断时,CPU 进入 EL1 的 Sync + EL0 入口(VBAR_EL1 + 0x000)。当内核态代码触发缺页中断时,CPU 进入 EL1 的 Sync + SP_EL1 入口(VBAR_EL1 + 0x400)。Linux 内核在 arch/arm64/kernel/entry.S 中定义 vectors 标签,并通过 msr vbar_el1, x0 设置向量表基址。
ARM64 异常向量表结构(EL1 示例) VBAR_EL1(向量表基址寄存器) +0x000 Sync (EL0) +0x080 IRQ (EL0) +0x100 FIQ (EL0) +0x180 SError (EL0) +0x200 Sync (SP_EL0) +0x280 IRQ (SP_EL0) +0x300 FIQ (SP_EL0) +0x380 SError (SP_EL0) +0x400 Sync (SP_EL1) +0x480 IRQ (SP_EL1) +0x500 FIQ (SP_EL1) +0x580 SError (SP_EL1) +0x600 Sync (AArch32) +0x680 IRQ (AArch32) +0x700 FIQ (AArch32) +0x780 SError (AArch32) 缺页中断入口:用户态缺页 → VBAR_EL1 + 0x000(Sync + EL0)| 内核态缺页 → VBAR_EL1 + 0x400(Sync + SP_EL1) Linux 内核向量表定义(arch/arm64/kernel/entry.S) .align 11 .globl vectors vectors: // 每个入口 128 字节,共 16 个入口

2.4 异常处理核心流程

当 ARM64 CPU 检测到异常时,硬件自动执行以下序列:

  1. 确定目标异常等级:根据异常类型和当前配置,决定路由到 EL1/EL2/EL3。
  2. 保存上下文:将返回地址写入 ELR_ELx,将 PSTATE 写入 SPSR_ELx。
  3. 记录异常信息:将异常原因写入 ESR_ELx,将故障虚拟地址写入 FAR_ELx(仅数据/指令异常)。
  4. 更新 PSTATE:设置 PSTATE 为目标异常等级,禁用 IRQ/FIQ(可选),切换栈指针。
  5. 跳转向量表:根据 VBAR_ELx + 偏移量,跳转到对应的异常处理入口。
  6. 软件处理:内核在入口处保存完整寄存器上下文,调用 C 语言处理函数。
  7. 异常返回:执行 ERET 指令,从 ELR_ELx 恢复 PC,从 SPSR_ELx 恢复 PSTATE。
缺页中断特殊性:缺页中断处理完成后,必须通过 ERET 重新执行触发异常的指令。因为 ELR_ELx 保存的是触发异常的指令地址,ERET 后 CPU 会重新尝试该指令,此时页表已经更新,MMU 可以成功完成地址转换。
ARM64 异常处理完整流程(硬件 + 软件) 1. 异常触发 2. 硬件保存 ELR/SPSR 3. 记录 ESR/FAR 4. 跳转向量表入口 5. 软件处理(内核) ← 上一章 📖 返回目录 下一章 →