2. ARMv8 异常模型:异常等级与向量表

ARMv8 架构的异常模型是理解 GICv3 中断控制器驱动的基础。异常(Exception)是处理器在执行指令流时,由于内部或外部事件而需要暂停当前程序流,转而执行特定处理程序(Handler)的机制。ARMv8 通过异常等级(Exception Level, EL)和异常向量表(Exception Vector Table)实现了对系统资源的分级保护与隔离。

2.1 异常等级(EL0 – EL3)

ARMv8 定义了四个异常等级,数字越大,特权越高。每个异常等级拥有独立的虚拟内存地址空间、系统寄存器视图和物理地址空间访问权限。

异常等级 名称 典型用途 执行状态
EL0 用户态(User) 应用程序(App) AArch64 / AArch32
EL1 内核态(Kernel) 操作系统内核(Linux Kernel) AArch64 / AArch32
EL2 虚拟机监控器(Hypervisor) 虚拟化层(KVM / Xen) AArch64
EL3 安全监控器(Secure Monitor) 安全固件(ARM Trusted Firmware) AArch64
关键概念:在 GICv3 中断控制器驱动中,中断处理程序通常运行在 EL1(内核态)。当发生安全中断(Secure Interrupt)时,处理会陷入 EL3(安全监控器),由 ATF 进行路由和分发。

异常等级之间通过异常陷入(Exception Trap)和异常返回(ERET)进行切换。低等级向高等级切换称为“陷入”,高等级向低等级切换称为“返回”。

异常陷入条件:
  • 执行 SVC(Supervisor Call)指令:从 EL0 陷入 EL1。
  • 执行 HVC(Hypervisor Call)指令:从 EL1 陷入 EL2。
  • 执行 SMC(Secure Monitor Call)指令:从 EL2 陷入 EL3。
  • 外部中断(IRQ / FIQ)或系统错误(SError)触发异步异常。
ARMv8 异常等级分层架构 EL3 — 安全监控器(Secure Monitor) EL2 — 虚拟机监控器(Hypervisor) EL1 — 操作系统内核(Kernel) EL0 — 用户态应用程序(App) SVC HVC SMC ERET ERET 异常陷入:低等级 → 高等级(SVC/HVC/SMC/中断) | 异常返回:高等级 → 低等级(ERET)

2.2 同步异常与异步异常

ARMv8 将异常分为两大类:同步异常(Synchronous Exception)和异步异常(Asynchronous Exception)。两者的根本区别在于异常的产生是否与当前正在执行的指令直接相关。

特性 同步异常 异步异常
触发源 指令执行本身(如未定义指令、数据中止) 外部事件(如中断、系统错误)
可预测性 可预测,与指令流强相关 不可预测,随机发生
返回地址 指向触发异常的指令(或下一条) 指向被中断的指令
典型例子 SVC、HVC、SMC、Page Fault、Alignment Fault IRQ、FIQ、SError
驱动开发注意:在中断控制器驱动中,GICv3 产生的 IRQ 和 FIQ 属于异步异常。这意味着中断处理程序必须保存和恢复被中断的上下文,不能假设处理器状态与中断发生前一致。

2.3 异常向量表基础

异常向量表(Exception Vector Table, EVT)是 ARMv8 处理器在发生异常时,用于查找处理程序入口地址的跳转表。每个异常等级拥有独立的向量表,基地址由 VBAR_ELn(Vector Base Address Register)寄存器指定。

每个异常等级的向量表包含 16 个入口,每个入口占用 128 字节(32 条指令)。16 个入口按照以下四个维度组织:

  • 异常类型:同步异常、IRQ、FIQ、SError。
  • 异常来源:来自当前 EL(Same EL)或来自低 EL(Lower EL)。
  • 栈指针选择:使用 SP_ELn(当前 EL 的栈指针)或 SP_EL0(EL0 的栈指针)。
  • 执行状态:AArch64 或 AArch32。

向量表布局如下(以 AArch64 为例):

偏移 异常类型 描述
0x000 Sync (Current EL, SP_ELn) 当前 EL 同步异常,使用 SP_ELn
0x080 IRQ (Current EL, SP_ELn) 当前 EL IRQ 中断,使用 SP_ELn
0x100 FIQ (Current EL, SP_ELn) 当前 EL FIQ 中断,使用 SP_ELn
0x180 SError (Current EL, SP_ELn) 当前 EL 系统错误,使用 SP_ELn
0x200 Sync (Current EL, SP_EL0) 当前 EL 同步异常,使用 SP_EL0
0x280 IRQ (Current EL, SP_EL0) 当前 EL IRQ 中断,使用 SP_EL0
0x300 FIQ (Current EL, SP_EL0) 当前 EL FIQ 中断,使用 SP_EL0
0x380 SError (Current EL, SP_EL0) 当前 EL 系统错误,使用 SP_EL0
0x400 Sync (Lower EL, AArch64) 低 EL 同步异常,目标为 AArch64
0x480 IRQ (Lower EL, AArch64) 低 EL IRQ 中断,目标为 AArch64
0x500 FIQ (Lower EL, AArch64) 低 EL FIQ 中断,目标为 AArch64
0x580 SError (Lower EL, AArch64) 低 EL 系统错误,目标为 AArch64
0x600 Sync (Lower EL, AArch32) 低 EL 同步异常,目标为 AArch32
0x680 IRQ (Lower EL, AArch32) 低 EL IRQ 中断,目标为 AArch32
0x700 FIQ (Lower EL, AArch32) 低 EL FIQ 中断,目标为 AArch32
0x780 SError (Lower EL, AArch32) 低 EL 系统错误,目标为 AArch32
向量表配置示例(Linux 内核):
/* arch/arm64/kernel/entry.S */
.align 11
ENTRY(vectors)
    ventry  el1_sync_invalid        // Sync EL1t
    ventry  el1_irq_invalid         // IRQ EL1t
    ventry  el1_fiq_invalid         // FIQ EL1t
    ventry  el1_error_invalid       // Error EL1t

    ventry  el1_sync                // Sync EL1h
    ventry  el1_irq                 // IRQ EL1h
    ventry  el1_fiq_invalid         // FIQ EL1h
    ventry  el1_error_invalid       // Error EL1h

    ventry  el0_sync                // Sync 64-bit EL0
    ventry  el0_irq                 // IRQ 64-bit EL0
    ventry  el0_fiq_invalid         // FIQ 64-bit EL0
    ventry  el0_error_invalid       // Error 64-bit EL0

    ventry  el0_sync_compat         // Sync 32-bit EL0
    ventry  el0_irq_compat          // IRQ 32-bit EL0
    ventry  el0_fiq_invalid         // FIQ 32-bit EL0
    ventry  el0_error_invalid       // Error 32-bit EL0
END(vectors)
ARMv8 异常向量表结构(AArch64) VBAR_ELn 基地址 0x000 Sync (Current EL, SP_ELn) 0x080 IRQ (Current EL, SP_ELn) 0x100 FIQ (Current EL, SP_ELn) 0x180 SError (Current EL, SP_ELn) 0x200 Sync (Current EL, SP_EL0) 0x280 IRQ (Current EL, SP_EL0) 0x300 FIQ (Current EL, SP_EL0) 0x380 SError (Current EL, SP_EL0) 0x400 Sync (Lower EL, AArch64) 0x480 IRQ (Lower EL, AArch64) 0x500 FIQ (Lower EL, AArch64) 0x580 SError (Lower EL, AArch64) 每个入口 128 字节(0x80),共 16 个入口,总大小 2KB(0x800)

2.4 异常处理流程与中断路由

当 GICv3 向处理器发送中断信号时,ARMv8 异常模型按照以下步骤处理:

  1. 中断信号到达:GICv3 通过 IRQ 或 FIQ 线向处理器核发送中断请求。
  2. 处理器状态保存:处理器自动将 PSTATE(程序状态寄存器)保存到 SPSR_ELn,并将返回地址保存到 ELR_ELn。
  3. 向量表查找:根据当前异常等级和中断类型,从 VBAR_ELn 指向的向量表中找到对应的入口。
  4. 跳转执行:处理器跳转到向量表入口,执行中断处理程序。
  5. 中断处理:在 Linux 内核中,el1_irq 或 el0_irq 入口会调用 handle_arch_irq,最终进入 GICv3 驱动的中断分发函数。
  6. 异常返回:处理完成后,执行 ERET 指令,从 ELR_ELn 恢复返回地址,从 SPSR_ELn 恢复 PSTATE,返回被中断的程序。
GICv3 与异常等级的关联:GICv3 支持将中断配置为 Group 0(安全中断,路由到 EL3)或 Group 1(非安全中断,路由到 EL1/EL2)。在 Linux 内核中,所有普通设备中断均配置为 Group 1,通过 IRQ 线注入 EL1。
ARMv8 中断处理流程(GICv3 触发) GICv3 发送 IRQ 保存 PSTATE → SPSR_ELn VBAR_ELn + 偏移 → 向量表入口 执行 ERET 返回被中断程序 整个流程由硬件自动完成,无需软件干预(除中断处理程序本身)

2.5 本章小结

  • ARMv8 定义了 EL0 ~ EL3 四个异常等级,实现从用户态到安全监控器的分级保护。
  • 异常分为同步异常(指令相关)和异步异常(外部事件),GICv3 中断属于异步异常。
  • 异常向量表是中断处理的入口,每个异常等级拥有独立的 16 入口 × 128 字节 的向量表。
  • 中断处理流程包括:保存上下文 → 向量表查找 → 执行处理程序 → ERET 返回。