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

3.3 向量表结构 SVG 图

ARM64 异常向量表结构(VBAR_ELn 指向) VBAR_ELn 基地址 向量表(256 字节,2KB 对齐) Synchronous 偏移 0x000 IRQ 偏移 0x080 FIQ 偏移 0x100 SError 偏移 0x180 SP0 +0x000 SPx +0x040 Lower AArch64 +0x080 Lower AArch32 +0x0C0 16 字节 (4 条指令) 入口代码 16 字节 (4 条指令) 入口代码 16 字节 (4 条指令) 入口代码 16 字节 (4 条指令) 入口代码

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 中向量表配置流程

  1. 定义 .align 11 — 确保向量表 2KB 对齐。
  2. 定义标签 — 如 vectors: 作为向量表起始。
  3. 填充 16 个表项 — 每个表项使用 .org 或手动偏移对齐。
  4. 写入 VBAR_EL1 — 使用 MSR VBAR_EL1, X0 加载基地址。
  5. 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(未知原因),通常指示向量表基地址配置错误。