ARMv7 处理器模式与寄存器组:七种模式详解
ARMv7 的处理器模式,说白了就是 CPU 的几套「工作服」。不同模式下,CPU 能看到不同的寄存器,权限也不一样。我刚开始学 ARM 时,总觉得模式切换很玄乎,后来调试一个中断嵌套的 bug,才真正体会到——不理解模式,你连 PC 怎么跳的都看不懂。
七种处理器模式
ARMv7 定义了 7 种运行模式。其中 6 种是特权模式,1 种是非特权模式。特权模式可以访问所有资源,非特权模式(用户模式)则受限。
| 模式 | 缩写 | 权限 | 典型用途 |
|---|---|---|---|
| 用户模式 | USR | 非特权 | 应用程序运行 |
| 快速中断模式 | FIQ | 特权 | 高速数据传输 |
| 普通中断模式 | IRQ | 特权 | 通用中断处理 |
| 管理模式 | SVC | 特权 | 系统调用(SWI) |
| 中止模式 | ABT | 特权 | 数据/指令访问异常 |
| 未定义模式 | UND | 特权 | 未定义指令陷阱 |
| 系统模式 | SYS | 特权 | 操作系统内核任务 |
通用寄存器组(R0-R15)
ARMv7 有 16 个通用寄存器,编号 R0 到 R15。但不同模式下,你看到的「R13」和「R14」可能不是同一个物理寄存器。这就是所谓的寄存器组备份。
R0-R12:通用数据寄存器
- R0-R7:所有模式共享同一份物理寄存器。没有备份。
- R8-R12:FIQ 模式有自己独立的 R8_fiq ~ R12_fiq。其他模式共享同一份。
为什么 FIQ 要单独备份 R8-R12?因为 FIQ 追求快速响应,省去了压栈保存的步骤。我做过一个高速数据采集的项目,FIQ 里直接操作 R8-R12,中断延迟能压到 10 个时钟周期以内。
R13(SP):栈指针
每种特权模式都有自己的 SP。用户模式和系统模式共享 SP_usr。这意味着:
- IRQ 模式有自己的栈
- FIQ 模式有自己的栈
- SVC 模式有自己的栈
- ……
R14(LR):链接寄存器
LR 保存子程序调用的返回地址。每种异常模式也有自己的 LR。例如:
- 执行 BL 指令时,LR = PC + 4
- IRQ 触发时,LR_irq = 返回地址
- SVC 触发时,LR_svc = 返回地址
嗯,这里要注意:异常模式的 LR 可能比实际返回地址多 4 或 8 个字节,具体要看异常类型。调试时别直接拿 LR 当返回地址用,得先修正。
R15(PC):程序计数器
PC 指向当前正在取指的指令地址。ARM 状态时,PC = 当前指令地址 + 8(因为三级流水线)。Thumb 状态时,PC = 当前指令地址 + 4。
程序状态寄存器(CPSR / SPSR)
CPSR 是当前程序状态寄存器,SPSR 是备份程序状态寄存器。每种异常模式都有自己的 SPSR,用于保存进入异常前的 CPSR。
CPSR 位域详解
| 位域 | 名称 | 说明 |
|---|---|---|
| [31:28] | N, Z, C, V | 条件标志位(负、零、进位、溢出) |
| [27] | Q | 饱和运算溢出标志(DSP 扩展) |
| [26:25] | IT[1:0] | Thumb-2 IT 指令状态 |
| [24:23] | J, GE[3:0] | Jazelle 状态 / SIMD 大于等于标志 |
| [9:8] | E, A | 大小端控制 / 异步中止屏蔽 |
| [7] | I | IRQ 中断屏蔽(1=屏蔽) |
| [6] | F | FIQ 中断屏蔽(1=屏蔽) |
| [5] | T | Thumb 状态(1=Thumb,0=ARM) |
| [4:0] | M | 处理器模式编码 |
M[4:0] 的编码值:
- 10000 = 用户模式
- 10001 = FIQ 模式
- 10010 = IRQ 模式
- 10011 = SVC 模式
- 10111 = 中止模式
- 11011 = 未定义模式
- 11111 = 系统模式
info registers 看 CPSR,或者直接 p/x $cpsr。如果看到 T 位是 1,但反汇编显示的是 ARM 指令,那肯定是模式不匹配。这种问题在混合编程时特别常见。
SPSR:异常现场的「快照」
当异常发生时,硬件自动将 CPSR 拷贝到当前异常模式的 SPSR 中。异常返回时,用 SPSR 恢复 CPSR。举个例子:
; IRQ 异常入口
IRQ_Handler:
; 此时 SPSR_irq 保存了被中断任务的 CPSR
; 处理中断...
SUBS PC, R14_irq, #4 ; 返回并恢复 CPSR
注意那个 SUBS PC, LR, #4。S 后缀表示操作结果会更新 CPSR,但这里实际上是从 SPSR 恢复。ARM 的这条指令是特例——当 PC 为目标寄存器且带 S 后缀时,自动将 SPSR 拷贝到 CPSR。
知识结构图:ARMv7 模式与寄存器关系
实战要点总结
- 模式切换:用户模式不能直接切换模式,必须通过异常或 SWI 指令。系统模式可以自由切换,常用于 OS 内核。
- 栈初始化:每个特权模式都要分配独立的栈空间。我一般给 IRQ 和 FIQ 分配 256 字节,SVC 分配 1KB,ABT 和 UND 分配 128 字节。
- CPSR 保护:中断处理函数里不要修改 CPSR 的 I/F 位,除非你清楚后果。我曾经在 FIQ 里清了 I 位,结果 IRQ 嵌套进来,栈直接爆了。
- SPSR 只读:异常模式下,SPSR 由硬件自动维护。手动修改 SPSR 会导致返回后状态异常。
- 调试利器:用 JTAG/SWD 读 CPSR 的 M[4:0] 位,可以快速判断 CPU 当前在哪个模式。如果发现 CPU 卡在 ABT 或 UND 模式,那基本就是访问了非法地址或执行了非法指令。