head S阶段异常向量表配置与ARM64启动调试 - 第2节
2.1 异常等级(Exception Levels, EL0–EL3)
ARMv8‑A 架构通过四个异常等级(Exception Level)实现特权分级,从最低权限的 EL0 到最高权限的 EL3。每个等级对应不同的执行环境与资源访问能力,是系统安全与隔离的基础。
核心原则:数值越大的异常等级,特权越高。低等级不能直接访问高等级的资源,必须通过异常切换等级。
2.1.1 异常等级定义与用途
| 异常等级 | 执行状态 | 典型用途 | 特权范围 |
|---|---|---|---|
| EL0 | AArch64 / AArch32 | 用户态应用程序 | 受限:无系统寄存器访问,仅可执行非特权指令 |
| EL1 | AArch64 / AArch32 | 操作系统内核(Linux、RTOS) | 系统寄存器、MMU、中断控制、虚拟内存管理 |
| EL2 | AArch64 / AArch32 | 虚拟机监视器(Hypervisor) | 虚拟化扩展、两阶段地址翻译、VM 切换 |
| EL3 | AArch64 强制 | 安全监控器(Secure Monitor) | 安全世界切换、电源管理、TrustZone 控制 |
启动阶段注意:ARM64 Linux 内核在
head.S 中从 EL2 或 EL3 降级到 EL1 后,才正式初始化内核页表与异常向量表。
2.1.2 异常等级切换关系图
2.2 异常类型(Exception Types)
ARMv8‑A 将异常分为四大类,每类对应不同的触发源与处理方式。理解这些类型是配置异常向量表的前提。
2.2.1 四种异常类型对比
| 异常类型 | 缩写 | 触发源 | 典型场景 | 向量表偏移 |
|---|---|---|---|---|
| Synchronous | Sync | 指令执行直接导致 | 系统调用(SVC)、数据中止、未定义指令、页故障 | 0x000 |
| IRQ | IRQ | 外部中断(普通) | 外设中断、定时器中断 | 0x080 |
| FIQ | FIQ | 外部中断(快速) | 高优先级外设、安全中断 | 0x100 |
| SError | SError | 系统错误 | 内存 ECC 错误、总线错误、异步外部中止 | 0x180 |
关键区分:Synchronous 异常与当前指令同步,返回地址指向导致异常的指令;IRQ/FIQ/SError 是异步的,返回地址指向下一条未执行的指令。
2.2.2 异常类型触发关系图
2.3 异常路由机制(Routing Mechanism)
异常路由决定一个异常由哪个异常等级处理。ARMv8‑A 通过系统寄存器 SCR_EL3、HCR_EL2 以及 RVBAR_ELn 控制路由行为。
2.3.1 路由控制寄存器
| 寄存器 | 所在等级 | 控制位 | 作用 |
|---|---|---|---|
SCR_EL3 | EL3 | IRQ, FIQ, EA, SMD | 决定 IRQ/FIQ/SError 是否路由到 EL3 |
HCR_EL2 | EL2 | IMO, FMO, AMO, TGE | 决定 IRQ/FIQ/SError 是否路由到 EL2 |
RVBAR_ELn | EL1/2/3 | 复位向量基地址 | 指定各异常等级的复位入口地址 |
2.3.2 路由决策流程
- 异常触发:CPU 检测到异常事件(同步或异步)。
- 等级判定:根据异常类型,检查当前异常等级的路由控制位。
- 路由选择:
- 若 SCR_EL3.IRQ=1,IRQ 路由到 EL3;否则由 EL2 或 EL1 处理。
- 若 HCR_EL2.IMO=1,IRQ 路由到 EL2;否则由 EL1 处理。
- FIQ 与 SError 类似,分别受 FMO/AMO 控制。
- 向量表查找:根据目标异常等级与异常类型,从
VBAR_ELn指向的向量表中取出对应入口。 - 等级切换:硬件自动保存 PSTATE 到 SPSR_ELn,设置返回地址到 ELR_ELn,跳转到向量入口。
2.3.3 路由决策流程图
启动调试提示:在
head.S 早期阶段,EL2/EL3 的路由控制位通常被清零,确保所有异常直接路由到 EL1,简化向量表配置。
2.4 异常向量表结构(Vector Table Layout)
每个异常等级拥有独立的向量表,基地址由 VBAR_ELn 寄存器指定。每个表包含 16 个入口,每个入口占用 0x80 字节(128 字节)。
2.4.1 向量表布局
| 偏移 | 异常类型 | 触发条件 | 说明 |
|---|---|---|---|
| 0x000 | Sync | 当前 EL,使用 SP_EL0 | EL0 模式下的同步异常 |
| 0x080 | IRQ | 当前 EL,使用 SP_EL0 | EL0 模式下的 IRQ |
| 0x100 | FIQ | 当前 EL,使用 SP_EL0 | EL0 模式下的 FIQ |
| 0x180 | SError | 当前 EL,使用 SP_EL0 | EL0 模式下的 SError |
| 0x200 | Sync | 当前 EL,使用 SP_ELx | EL1/2/3 模式下的同步异常 |
| 0x280 | IRQ | 当前 EL,使用 SP_ELx | EL1/2/3 模式下的 IRQ |
| 0x300 | FIQ | 当前 EL,使用 SP_ELx | EL1/2/3 模式下的 FIQ |
| 0x380 | SError | 当前 EL,使用 SP_ELx | EL1/2/3 模式下的 SError |
| 0x400 | Sync | 低等级 AArch64 | 从 AArch64 低等级进入的同步异常 |
| 0x480 | IRQ | 低等级 AArch64 | 从 AArch64 低等级进入的 IRQ |
| 0x500 | FIQ | 低等级 AArch64 | 从 AArch64 低等级进入的 FIQ |
| 0x580 | SError | 低等级 AArch64 | 从 AArch64 低等级进入的 SError |
| 0x600 | Sync | 低等级 AArch32 | 从 AArch32 低等级进入的同步异常 |
| 0x680 | IRQ | 低等级 AArch32 | 从 AArch32 低等级进入的 IRQ |
| 0x700 | FIQ | 低等级 AArch32 | 从 AArch32 低等级进入的 FIQ |
| 0x780 | SError | 低等级 AArch32 | 从 AArch32 低等级进入的 SError |
2.4.2 向量表结构图
head.S 实践:Linux 内核在
__primary_switch 之前通过 __setup_vector_table 将 VBAR_EL1 设置为 _vectors 标签地址,确保异常向量表在 MMU 开启前生效。