3. 异常向量表(Vector Table)原理
异常向量表是 ARM64 处理器在发生异常(中断、缺页、系统调用等)时,用于定位异常处理程序入口地址的核心数据结构。在 Head.S 启动阶段,正确配置向量表是系统稳定运行的前提。
3.1 基地址寄存器 VBAR_ELn
ARM64 为每个异常级别(EL3、EL2、EL1)提供了独立的向量表基地址寄存器:
| 寄存器 | 所属异常级别 | 典型使用场景 |
|---|---|---|
| VBAR_EL3 | EL3(安全监控器) | 安全启动、PSCI、Secure Monitor |
| VBAR_EL2 | EL2(虚拟机监控器) | 虚拟化、KVM、Type-1 Hypervisor |
| VBAR_EL1 | EL1(操作系统内核) | Linux 内核、RTOS 异常处理 |
关键点:VBAR_ELn 寄存器保存的是向量表的物理基地址,且必须满足 2KB 对齐(低 11 位为 0)。在 Head.S 中,通常使用
MSR VBAR_EL1, X0 指令写入。
3.2 向量表偏移与表项大小
ARM64 向量表具有严格的固定结构:
- 表项大小:每个异常向量占用 16 字节(4 条指令)。
- 表项数量:共 16 个表项,覆盖 4 种异常类型 × 4 种异常来源。
- 总大小:16 × 16 = 256 字节。
- 对齐要求:向量表基地址必须 2KB 对齐(0x7FF 位为 0)。
向量表偏移计算规则:
异常入口地址 = VBAR_ELn + 偏移量
偏移量 = (异常类型偏移) + (异常来源偏移)
异常类型偏移:
0x000 — Synchronous(同步异常)
0x080 — IRQ(普通中断)
0x100 — FIQ(快速中断)
0x180 — SError(系统错误)
异常来源偏移(在每种类型内):
0x000 — Current EL with SP0
0x040 — Current EL with SPx
0x080 — Lower EL using AArch64
0x0C0 — Lower EL using AArch32
示例:若 VBAR_EL1 = 0x80080000,则来自 Lower EL(用户态)的同步异常入口地址为:
0x80080000 + 0x000(同步) + 0x080(Lower AArch64) = 0x80080080
0x80080000 + 0x000(同步) + 0x080(Lower AArch64) = 0x80080080
3.3 向量表结构 SVG 图
3.4 表项内容与指令布局
每个 16 字节的表项通常包含以下指令序列:
| 偏移 | 指令 | 说明 |
|---|---|---|
| +0x00 | b el1_sync_handler |
跳转到同步异常处理函数 |
| +0x04 | nop |
填充(或备用指令) |
| +0x08 | nop |
填充 |
| +0x0C | nop |
填充 |
重要约束:由于每个表项只有 16 字节(4 条指令),如果处理函数入口地址超出分支指令的跳转范围(±128MB),则必须使用
ldr 加载绝对地址或使用 adrp + add 组合。Head.S 中通常使用 ldr x0, =handler_addr; br x0 模式。
3.5 Head.S 中向量表配置流程
- 定义 .align 11 — 确保向量表 2KB 对齐。
- 定义标签 — 如
vectors:作为向量表起始。 - 填充 16 个表项 — 每个表项使用
.org或手动偏移对齐。 - 写入 VBAR_EL1 — 使用
MSR VBAR_EL1, X0加载基地址。 - ISB 同步 — 执行
ISB指令确保配置生效。
3.6 向量表配置代码示例
.section .text.vectors, "ax"
.align 11 /* 2KB 对齐 */
.globl vectors
vectors:
/* 当前 EL,SP0 — 同步异常 */
b el1_sync_sp0
.align 7 /* 跳到下一个表项起始 */
/* 当前 EL,SP0 — IRQ */
b el1_irq_sp0
.align 7
/* 当前 EL,SP0 — FIQ */
b el1_fiq_sp0
.align 7
/* 当前 EL,SP0 — SError */
b el1_serror_sp0
.align 7
/* 当前 EL,SPx — 同步异常 */
b el1_sync_spx
.align 7
/* 当前 EL,SPx — IRQ */
b el1_irq_spx
.align 7
/* 当前 EL,SPx — FIQ */
b el1_fiq_spx
.align 7
/* 当前 EL,SPx — SError */
b el1_serror_spx
.align 7
/* Lower EL,AArch64 — 同步异常 */
b el1_sync_l64
.align 7
/* Lower EL,AArch64 — IRQ */
b el1_irq_l64
.align 7
/* Lower EL,AArch64 — FIQ */
b el1_fiq_l64
.align 7
/* Lower EL,AArch64 — SError */
b el1_serror_l64
.align 7
/* Lower EL,AArch32 — 同步异常 */
b el1_sync_l32
.align 7
/* Lower EL,AArch32 — IRQ */
b el1_irq_l32
.align 7
/* Lower EL,AArch32 — FIQ */
b el1_fiq_l32
.align 7
/* Lower EL,AArch32 — SError */
b el1_serror_l32
/* 在启动汇编中配置 VBAR_EL1 */
adrp x0, vectors
add x0, x0, #:lo12:vectors
msr vbar_el1, x0
isb
3.7 常见配置错误与调试
| 错误类型 | 现象 | 根因 | 解决方案 |
|---|---|---|---|
| 对齐错误 | 异常直接进入 EL3 Undefined | VBAR_ELn 低 11 位非零 | 检查 .align 11 或手动清零低 11 位 |
| 表项偏移错误 | 异常处理函数执行异常代码 | 表项间未正确 .align 7 |
确保每个表项起始地址偏移正确 |
| 跳转超范围 | 链接时报 relocation 错误 | b 指令范围 ±128MB 不足 |
改用 ldr + br 绝对跳转 |
| ISB 缺失 | 配置后首次异常仍进入旧向量表 | 写 VBAR 后未执行 ISB | 在 MSR 后立即添加 ISB |
调试技巧:在 QEMU 或真实硬件上,可通过检查
ESR_EL1(异常综合症寄存器)和 ELR_EL1(异常链接寄存器)来定位向量表配置问题。若 ESR_EL1.EC 为 0x00(未知原因),通常指示向量表基地址配置错误。