当前位置: 首页 > news >正文

深入理解 ARMv7-A|异常/中断处理

参考:
《ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition》
《ARM Cortex™-A Series Version: 4.0 Programmer’s Guide》

ARMv7-A 系列其他文章:

深入理解 ARMv7-A|处理器模式与寄存器

1、ARMv7-A 异常概述

异常(Exception)是指任何需要核心暂停当前正常执行流、转而运行一段专用特权代码(异常处理程序,Exception Handler)的条件或系统事件。处理完成后,特权软件负责准备核心恢复到异常发生前的状态继续执行。

在很多资料里,exceptioninterrupttrap这几个词经常混着用,但在 ARM 语境里最好区分开:

  • Exception:所有打断正常执行流的事件的统称
  • Interrupt:特指 IRQ 和 FIQ 这两种异步硬件中断
  • Trap:非 ARM 官方术语,在虚拟化语境下有时指 Hyp Trap

从程序员视角看,理解异常机制,本质上就是回答三个问题:

  1. 从哪来—— 什么事件触发了异常?
  2. 到哪去—— 处理器跳到哪个地址执行处理程序?
  3. 怎么回—— 处理完毕后如何恢复被中断的程序?

ARMv7-A 的异常处理有一个很重要的特点:它和处理器模式是紧密绑定的。不同类型的异常,会让处理器自动进入不同的模式;同时,硬件还会自动保存现场的一部分关键状态,例如:

  • 把当前CPSR保存到目标模式的SPSR
  • 把返回地址写入目标模式的LR
  • 根据异常类型设置中断屏蔽位
  • PC重定向到异常向量表对应入口

这也是 ARM 异常机制非常“硬件化”的地方:异常入口的大量基础动作,硬件已经替软件做掉了。

2、异常类型详解

ARMv7-A 常见的异常类型如下表所示。表中的“向量偏移”是相对于异常向量表基址而言的偏移,而不是绝对地址。

异常类型进入模式向量偏移触发方式同步/异步
ResetSVC0x00复位信号异步
Undefined InstructionUND0x04未定义指令 / 未知协处理器指令同步
SVCSVC0x08SVC指令同步
HVCHYP0x08(Hyp 向量表)HVC指令(虚拟化扩展)同步
SMCMON0x08(Monitor 向量表)SMC指令(安全扩展)同步
Prefetch AbortABT0x0C指令预取失败同步
Data AbortABT0x10数据访问失败同步
Hyp TrapHYP0x14Hyp 模式下的异常入口(虚拟化)
IRQIRQ0x18普通中断信号异步
FIQFIQ0x1C快速中断信号异步

说明

  • ARMv7-A 在引入安全扩展和虚拟化扩展后,可能存在多套向量表,例如 Non-secure、Secure、Monitor、Hyp。
  • 上表中的偏移值,例如0x180x1C,都只是“表内偏移”,实际入口地址还要结合向量表基址一起看。

2.1 IRQ 与 FIQ

ARMv7-A 提供两类外部中断请求信号:

  • IRQ(Interrupt Request):普通中断,系统中绝大多数外设中断都走这条路径。
  • FIQ(Fast Interrupt Request):快速中断,优先级高于IRQ,并且具备硬件层面的速度优势:FIQ 模式拥有私有的 R8-R12,处理程序无需压栈即可直接使用,进一步减少时钟周期开销

设计原则:FIQ 预留给单一的、需要保证快速响应时间的高优先级中断源,并且 FIQ 处理程序被期望不再产生任何其他异常(不能有 SVC 调用,不能触发缺页等)。这是因为 FIQ 不会禁用自身,如果再产生新的异常,处理将变得极其复杂。

一个关键的差异在于中断屏蔽行为:

  • 进入IRQ模式时,硬件自动置位CPSR.I = 1,也就是屏蔽后续IRQ;但CPSR.F不会因此自动置位,所以FIQ仍然可以抢占IRQ
  • 进入FIQ模式时,硬件会同时置位CPSR.F = 1CPSR.I = 1,也就是在处理FIQ期间同时屏蔽FIQIRQ

在 Linux 世界里,FIQ并不是通用主路径。主流 Linux 内核日常主要使用IRQ体系;FIQ往往只在一些非常特定的 SoC 方案、调试方案或安全方案里才会被使用。

部分 Cortex-A 处理器还支持把FIQ配置成NMFI(Non-Maskable FIQ)。在这种配置下,软件不能简单通过修改CPSR.F来长期屏蔽FIQ,这通常由硬件配置决定。

2.2 Abort(中止异常)

Abort 是最复杂的异常类型,由失败的内存访问触发。按来源可分为两个维度:

(1)按访问阶段

类型向量偏移触发时机LR 调整值
Prefetch Abort0x0C指令预取失败。异常在指令执行前触发。LR - 4
Data Abort0x10数据读写失败。异常在 load/store 指令尝试执行后触发。LR - 8

为什么两者的返回修正不同?根本原因在于ARM 流水线导致异常发生时的PC已经不是“当前那条指令的地址”

  • Prefetch Abort来说,处理器是在取指过程中发现问题,因此返回时通常用LR - 4回到故障指令。
  • Data Abort来说,错误是在数据访问阶段暴露出来的,此时流水线更靠后,因此通常要用LR - 8才能回到出问题的那条指令。

这也是为什么异常返回代码里经常会看到:

SUBS PC, LR, #4 ; Prefetch Abort / IRQ / FIQ 常见形式 SUBS PC, LR, #8 ; Data Abort 常见形式

(2)按同步属性

类型含义
同步 Abort由指令流执行直接触发,返回地址能够精确定位导致 abort 的那条指令。
精确异步 Abort外部内存系统报告的错误,但 abort 处理程序可以确定是哪条指令导致的,且此后再无其他指令执行。
不精确异步 Abort外部内存系统对某次无法识别的内存访问报告了错误。例如缓冲写操作收到错误响应时,之后已有其他指令被执行。处理程序无法定位问题指令,只能终止导致问题的进程。

MMU引起的权限错误、翻译错误,通常都属于同步 Abort。这类异常最适合做缺页处理、权限检查、进程杀死等标准操作系统路径。

不精确异步 Abort 更麻烦。它往往来自总线、写缓冲、外部内存系统错误,而且报错可能“晚到”。等异常真正进入时,处理器可能已经继续执行了后面的多条指令。

这时CPSR.A位就很重要了。它控制的是异步 abort 的屏蔽/延迟处理行为。操作系统会结合 barrier 指令,尽量把异步错误和正确的上下文对应起来,避免“前一个进程做错了事,后一个进程来背锅”。

如何定位 Abort 现场?

分析 Abort 时,最关键的通常有三类信息:

  • 故障状态:例如 CP15 的DFSR/IFSR
  • 故障地址:例如 CP15 的DFAR/IFAR
  • 返回地址:也就是进入异常时保存下来的LR_abt

其中:

  • DFSR(Data Fault Status Register)记录 Data Abort 的原因
  • DFAR(Data Fault Address Register)记录 Data Abort 的相关地址
  • IFSR/IFAR则对应取指相关故障

把这些寄存器信息和修正后的返回地址结合起来,异常处理程序才能判断:

  • 这是缺页,可以修页表后重试
  • 这是权限错误,应当向用户态抛异常
  • 这是总线错误或严重硬件故障,应当终止当前任务甚至停机

2.3 Reset

Reset是最高优先级异常。它不属于“程序运行过程中某条指令触发的异常”,而是整个处理器执行环境被重新拉回初始状态的入口。

复位后,处理器通常进入SVC模式,并从复位向量开始执行。常见的初始化工作包括:

  • 建立异常向量表
  • 初始化栈
  • 初始化时钟、内存控制器、串口等基础硬件
  • 初始化 MMU/Cache
  • 初始化 VFP/NEON
  • 为多核系统唤醒其他 CPU
  • 最后跳入main()或更上层的启动代码

如果是多核系统,通常并不是每个核都完整跑一遍同样的启动流程。更常见的做法是:主核负责系统初始化,从核先等待,后续再被主核唤醒

2.4 指令触发的异常

除了外部事件和硬件错误,ARM 里还有一类异常是软件主动触发的。它们本质上是在请求更高特权级提供服务。

指令进入模式用途扩展要求
SVCSVC (PL1)User 模式请求操作系统服务(系统调用)
HVCHYP (PL2)Guest OS 请求 Hypervisor 服务虚拟化扩展
SMCMON (PL1, Secure)Normal World 请求 Secure World 服务安全扩展

此外,任何核心无法识别的指令(包括不存在或不使能的协处理器指令)会触发UNDEFINED异常。这个机制的重要应用之一是指令模拟——比如硬件 VFP 未实现或不使能时,通过 UNDEFINED 异常处理程序来用软件模拟浮点运算。

在 Thumb 状态执行SVC时,取 SVC 立即数的方法和 ARM 状态不同。异常处理程序通常需要结合SPSR.T判断调用方来自 ARM 还是 Thumb,再决定如何解析原始指令编码。

3、异常优先级

当多个异常同时发生时,硬件按照固定的优先级顺序处理。每种异常进入时对CPSR.ICPSR.F屏蔽位的行为也不同:

优先级异常进入模式CPSR.ICPSR.F
最高ResetSVC11
Data AbortABT1不变
FIQFIQ11
IRQIRQ1不变
Prefetch AbortABT1不变
最低Undefined / SVCUND / SVC1不变

两个关键推论:

  1. FIQ 可以打断 IRQ 和 Abort 处理程序。如果 Data Abort 和 FIQ 同时发生,Data Abort(更高优先级)先被处理。因为 Data Abort 不屏蔽 FIQ(F 位不变),核心随后立即进入 FIQ 处理程序。FIQ 处理完毕后再返回继续 Data Abort。
  2. 未定义指令和 SVC 是互斥的——它们都由执行指令触发,一条指令不可能既是 SVC 又是未定义指令。同理,Prefetch Abort 标记了无效指令,也不可能与 Undef / SVC 同时发生。

注意:ARM 架构并未定义异步异常(FIQ、IRQ、异步 Abort)的确切采样时机。因此异步异常相对于同步异常的优先级实际上是implementation defined

4、异常向量表

异常向量表可以理解成:处理器遇到某类异常后,第一跳会去查的一张固定入口表

在经典 ARM 状态下,向量表由一组固定偏移的入口组成,每个入口只有 4 字节空间,因此通常放不下完整处理逻辑,而只是放一条跳转指令。

4.1 向量表布局

引入安全扩展和虚拟化扩展之后,ARMv7-A 可能维护多套向量表,分别用于不同执行环境:

向量偏移Normal 模式Secure 模式Hyp 模式Monitor 模式
0x00ResetReset
0x04UndefinedUndefinedUndefined (from Hyp)
0x08SVCSVCSMC
0x0CPrefetch AbortPrefetch AbortPrefetch Abort (from Hyp)Prefetch Abort
0x10Data AbortData AbortData Abort (from Hyp)Data Abort
0x14Hyp Trap Entry
0x18IRQIRQIRQIRQ
0x1CFIQFIQFIQFIQ

其中0x14这个偏移在传统 ARMv7-A 里是保留的;有了虚拟化扩展之后,它被 Hyp 模式用作专门的 trap 入口。

4.2 向量表基址配置

异常入口的“偏移”固定,但整张表放在哪里,是由基址决定的。

在 ARMv7-A 中,向量表基址主要由SCTLR.VVBAR共同决定:

SCTLR.V向量表基址来源说明
0VBAR向量表可放在任意物理/虚拟地址,由CP15 c12, c0, 0指定
10xFFFF0000高向量(HIVECS)模式,向后兼容旧 ARM 架构

这里要注意两个容易混淆的点:

  • VBAR并不是“任意值都行”,它本身有对齐要求。
  • 当启用高向量时,异常入口不再从VBAR + offset取,而是改为固定从高地址区域取。

如果处理器支持安全扩展或虚拟化扩展,还会额外有:

  • MVBAR:Monitor 向量表基址
  • HVBAR:Hyp 向量表基址


4.3 CP15 异常配置关键寄存器

寄存器CP15 编码用途
VBARc12, c0, 0Non-secure / Secure PL1 向量表基址(Banked)
MVBARc12, c0, 1Monitor 模式向量表基址
HVBARc12, c4, 0Hyp 模式向量表基址
SCTLR.Vc1, c0, 0bit 13向量表基址选择(0 = VBAR, 1 =0xFFFF0000
SCTLR.TEc1, c0, 0bit 30异常处理指令集(0 = ARM, 1 = Thumb)
SCTLR.EEc1, c0, 0bit 25异常处理字节序(0 = 小端, 1 = 大端)

Monitor 模式向量表基址寄存器:


支持 Hypervisor 扩展时的 Hyp 向量表基址寄存器:

4.4 向量表入口的两种跳转模式

每个异常入口只有 4 字节空间,因此向量表中几乎总是放置以下两种跳转指令之一:

(1)PC 相对分支

B <handler_label>

优点是快、简单;缺点是跳转范围有限。ARM 状态下B指令可覆盖大约±32MB的范围。

(2)间接加载

LDR PC, [PC, #offset]

它的思路是:先从附近取出一个绝对地址,再把这个地址装入PC。这样处理程序可以放在更远的位置,布局更灵活。

这也是很多内核源码里常见的写法。

5、异常进入与返回

5.1 硬件自动行为(异常进入)

当异常发生时,ARM 核心硬件自动完成以下四步操作——这些是程序员不需要写代码实现的:

Step 1: CPSR → SPSR_<mode> // 将当前状态保存在目标模式的 SPSR 中 Step 2: 返回地址 → LR_<mode> // 将返回地址写入目标模式的 LR(返回地址是异常发生时的 PC 值) Step 3: 修改 CPSR // 切换模式、设置 T/J/E 位、置中断屏蔽位 Step 4: PC → 向量表对应入口 // 跳转到异常处理程序

第三步中 CPSR 的变化细节值得展开:

  • CPSR.M[4:0]:设置为异常类型对应的处理器模式编码。
  • CPSR.{A, I, F}:根据异常类型对照表(见第 3 节)自动置位或保持不变。
  • CPSR.T:设置为SCTLR.TE的值——无论被中断的代码处于 ARM 还是 Thumb 状态,异常处理程序可以统一使用一种指令集
  • CPSR.J:清零。
  • CPSR.E:设置为SCTLR.EE的值,同样统一异常处理的字节序。

5.2 异常返回时,为什么总要修正 LR

从异常处理返回需要原子地完成两个操作:

  1. SPSR_<mode>恢复到 CPSR(恢复处理器模式、中断屏蔽位、指令集状态)
  2. 将 PC 设置为修正后的返回地址

ARM 的三级流水线意味着保存到LR_<mode>里的值,往往不是你想直接返回的最终地址移。更关键的是,ARM 架构要求Prefetch Abort 和 Undefined 异常必须能够重新执行导致异常的那条指令(例如缺页处理程序修复页表后要重新执行触发 abort 的 load/store),而非直接跳到下一条。这也意味着不同类型的异常需要不同的 LR 修正值:

异常类型LR 调整典型返回指令返回目标
SVC / UndefLR + 0MOVS PC, LRSVC: 下一条指令 / Undef: 重执行当前指令
Prefetch AbortLR - 4SUBS PC, LR, #4重新执行导致 abort 的指令
Data AbortLR - 8SUBS PC, LR, #8重新执行导致 abort 的指令
IRQ / FIQLR - 4SUBS PC, LR, #4被中断的下一条指令

注意SUBS中的S后缀:当目标寄存器是 PC 时,S后缀表示同时将 SPSR 恢复到 CPSR。在 ARM 架构中,这一条指令同时完成 PC 跳转和 CPSR 恢复,是整个异常返回机制的精髓。这也是MOVS PC, LR被特意设计为异常返回指令的原因——普通的MOV PC, LR不会恢复 SPSR。

5.3 三种返回方式

方式指令示例适用场景
数据处理指令 + SSUBS PC, LR, #4最简单的返回方式,处理程序未使用栈保存上下文
多寄存器加载 + ^LDMFD sp!, {R0-R12, PC}^处理程序入口将各寄存器保存在栈上,返回时统一恢复。^后缀表示同时恢复 SPSR
RFE 指令RFEFD sp!将栈上的 LR 和 SPSR 分别恢复到 PC 和 CPSR,一步完成。语义更清晰,推荐在新代码中使用

关键约束:不能使用 16 位 Thumb 指令返回异常,因为它们无法操作 CPSR/SPSR。异常返回必须使用 32 位 ARM 指令(或等价的 32 位 Thumb-2 指令)。

6、结合 Linux 源码理解向量表

6.1 Linux 中的异常向量表示例

Linux ARM 代码里,异常向量表通常会放在类似下面的位置:

arch/arm/kernel/entry-armv.S

.section .vectors, "ax", %progbits W(b) vector_rst W(b) vector_und ARM( .reloc ., R_ARM_LDR_PC_G0, .L__vector_swi ) THUMB( .reloc ., R_ARM_THM_PC12, .L__vector_swi ) W(ldr) pc, . W(b) vector_pabt W(b) vector_dabt W(b) vector_addrexcptn W(b) vector_irq W(b) vector_fiq

这段代码可以这样理解:

  • W(b) vector_rst:在 Reset 向量入口放一条跳转指令
  • W(b) vector_und:在 Undefined 向量入口放一条跳转指令
  • W(ldr) pc, .:在当前入口放一条ldr pc, ...形式的间接跳转
  • ARM(...)THUMB(...):分别为 ARM 和 Thumb 场景生成对应的重定位信息

可以把它理解成:向量表本身并不负责做复杂处理,它只负责把异常快速导流到真正的处理函数入口

这也是异常向量表设计的典型思路:

  • 向量表入口尽量短
  • 真正复杂的现场保存和异常分发,交给后面的 handler

如果你在读 Linux 源码时发现某些向量入口没有直接写成b handler,而是用了ldr pc, ...或重定位技巧,不要奇怪。那通常只是为了兼顾链接布局、Thumb 支持或地址范围限制,本质上仍然是在做“第一跳”。

http://www.jsqmd.com/news/882595/

相关文章:

  • 猫抓浏览器扩展:构建高效流媒体资源嗅探工作流的终极指南
  • Frida安卓逆向实战:从动态插桩到Native层Hook
  • 荣耀出征手游官网下载:奇迹MU最新官方22区5月30日13点火爆开区!!
  • 前缀和——高频考点:子数组和、区间和、和为 K 的子数组
  • 海工塔吊租赁选购指南:靠谱的高前景、高防辐射公司推荐 - mypinpai
  • 2026宿迁黄金 铂金 白银 彩金回收口碑榜出炉:这五家店稳居前列,靠谱又放心 - 前途无量YY
  • 告别分区焦虑:用GParted Live USB无损调整Ubuntu/Debian分区(附swapfile替代方案)
  • 告别黄牛票!5分钟配置大麦网自动化抢票神器
  • 2026宿州黄金 铂金 白银 彩金回收口碑榜出炉:这五家店稳居前列,靠谱又放心 - 前途无量YY
  • 从ACPI _SUN到物理槽位:深入Linux内核看PCIe插槽编号的诞生与管理
  • 周报5.24
  • 突破物理限制:用ParsecVDisplay在Windows上创建完美虚拟显示器
  • 飞书文档批量导出架构解析:如何设计一个企业级文档迁移工具
  • Tflite模型缓存优化与Arm Ethos-N78 NPU部署实践
  • 如何快速重置JetBrains IDE试用期:高效实用的完整解决方案
  • 2026随州黄金 铂金 白银 彩金回收口碑榜出炉:这五家店稳居前列,靠谱又放心 - 前途无量YY
  • BepInEx终极指南:如何快速上手Unity游戏插件框架的10个技巧
  • NS-USBLoader:Switch文件传输与RCM注入的一站式解决方案
  • 超详细AttentionTransformer:从原理到完整架构全覆盖
  • 3个步骤解锁QQ音乐加密文件:QMCDecode如何让你的音乐库重获自由?
  • 2026陇南黄金 铂金 白银 彩金回收口碑榜出炉:这五家店稳居前列,靠谱又放心 - 前途无量YY
  • 新书上架 | 黄仁勋是如何提前十年押注AI,助推英伟达登顶世界之巅的?
  • 终极免费方案:3分钟解决游戏按键冲突,让操作精度提升87%
  • 2026遂宁黄金 铂金 白银 彩金回收口碑榜出炉:这五家店稳居前列,靠谱又放心 - 前途无量YY
  • 老iMAC焕新记:不拆机不折腾,用三星T7移动固态硬盘让2015款iMac再战五年
  • UE5材质实例MI保姆级指南:如何像调PS滑块一样,实时调整游戏里的砖墙颜色和质感?
  • 别急着买云服务器!手把手教你将闲置Win10台式机改造成SSH远程开发机(保姆级教程)
  • 2026金华黄金 铂金 白银 彩金回收口碑榜出炉:这五家店稳居前列,靠谱又放心 - 前途无量YY
  • 用机器学习预测歌曲走红:从Spotify音频特征到Billboard榜单分析
  • Zotero_AI时代的数据查询和搜索