文章目录
- 概述
- 通用寄存器
- 程序状态寄存器
- NZCV标记位
- 特殊寄存器
- PC寄存器
- SP寄存器
- SPSR寄存器
- 异常链接寄存器(ELR)
- CurrentEL寄存器
- SPSel寄存器
- DAIF寄存器
- NZCV寄存器
- PAN寄存器
- UAO寄存器
- 零寄存器
- 系统寄存器
- 相关参考
概述
ARM体系架构下,程序运行依赖一系列的寄存器,用于记录运行状态、过程数据以及系统配置等,这些寄存器大体可分为以下几个类别:
- 通用寄存器
- 程序状态寄存器
- 特殊寄存器
- 系统寄存器。
通用寄存器
AArch64运行状态支持31个64位的通用寄存器,分别是X0~X30寄存器,而AArch32状态支持16个32位的通用寄存器。通用寄存器除了用于数据运算和存储之外,还可以在函数调用过程中起到特殊作用,ARM64架构的函数调用标准和规范对此有所约定。
在AArch64状态下,使用X来表示64位通用寄存器,如X0、X30等。另外,还可以使用W来表示低32位的数据,如W0表示X0寄存器的低32位数据,W1表示X1寄存器的低32位数据
通用寄存器中有2个相对特殊的寄存器X29和x30,主要用于函数调用过程,其中:
- X29:栈帧寄存器,可以通过别名
FP进行引用,用于记录函数调用栈帧的基地址; - X30:链接寄存器,可以通过别名
LR进行引用,用于保存函数调用的返回地址。
程序状态寄存器
在AArch64里使用PSTATE寄存器来表示,准确来说PSTATE并不是单一的寄存器,而是一系列系统状态的组合。
PSTATE状态通常是通过其它特殊的系统寄存器进行访问,包括CurrentEL、DAIF、SPSel、PAN、UAO以及NZCV寄存器,这些寄存器可以通过指令MRS/MSR进行访问和操作。
NZCV标记位
| 标志 | 全称 | 含义 | 触发条件 |
|---|---|---|---|
| Z | Zero | 结果全0 | 运算输出所有bit=0,Z=1;否则0 |
| N | Negative | 结果最高位 | 运算结果最高bit(bit63/X, bit31/W)=1 → N=1;视作有符号负数 |
| C | Carry | 进位/借位(无符号溢出) | ADD:最高位产生进位;SUB:最高位产生借位 |
| V | Overflow | 有符号补码溢出 | 两个同符号数相加,结果符号反转;两异号数相减等价同号相加溢出 |
- C 只管无符号溢出(纯二进制进位,和正负无关)
- V 只管补码有符号溢出(符号位出错)
- N/Z 只是结果属性,和溢出无关
特殊寄存器
ARMv8架构除了支持31个通用寄存器之外,还提供多个特殊的寄存器:
PC寄存器
PC寄存器通常用来指向当前运行指令的下一条指令的地址,用于控制程序中指令的运行顺序,ARM不支持通过指令来直接访问它,但使用分支跳转和返回指令可以间接改变。
SP寄存器
ARMv8架构支持4个异常等级,每一个异常等级都有一个专门的SP寄存器SP_ELn,存放当前异常等级下程序运行使用的栈地址,如处理器运行在EL1时选择SP_EL1寄存器作为SP寄存器。
- SP_EL0:EL0下的SP寄存器。
- SP_EL1:EL1下的SP寄存器。
- SP_EL2:EL2下的SP寄存器。
- SP_EL3:EL3下的SP寄存器。
当处理器运行在比EL0高的异常等级时,处理器可以访问如下寄存器。当前异常等级对应的SP寄存器SP_ELn。
EL0对应的SP寄存器SP_EL0可以当作一个临时寄存器,如Linux内核里使用该寄存器存放进程的task_struct数据结构的指针。
当处理器运行在EL0时,它只能访问SP_EL0,而不能访问其他高级的SP寄存器。
SPSR寄存器
当CPU进入异常处理时,硬件会自动把当前CPU的程序状态保存到SPSR(Saved Process Status Register)寄存器中。SPSR寄存器内容定义如下:
当异常将要发生时,CPU会把PSTATE的值暂时保存到SPSR里;当异常处理完成并返回时,CPU再把SPSR的值恢复到PSTATE。
异常链接寄存器(ELR)
ELR寄存器用于存放异常返回地址。当处理器发生异常时,会将异常返回地址保存在异常级别对应的ELR寄存器中。AArch64位每个异常级别都提供了ELR寄存器。
CurrentEL寄存器
CurrentEL寄存器用于访问PSTATE寄存器中的EL字段,EL保存了当前任务运行的异常等级,使用MRS指令可以读取当前异常等级:
MRS X0, CurrentEL其中:
- 0:表示EL0;
- 1:表示EL1;
- 2:表示EL2;
- 3:表示EL3。
SPSel寄存器
SPSel寄存器用于访问PSTATE寄存器中的SP字段,用于ELx异常等级(不包括EL0)选择SP寄存器,可以是SP_EL0或SP_ELx。
当PSTATE.SPSel = 0时,CPU使用SP_EL0作为当前任务的堆栈寄存器;反之则使用与异常等级对应的SP寄存器。
DAIF寄存器
DAIF寄存器用于访问PSTATE寄存器中的{D, A, I, F}字段,用于异常、中断以及调试开关控制。
NZCV寄存器
NZCV寄存器用于访问PSTATE寄存器中的{N, Z, C, V}字段。
PAN寄存器
PAN寄存器用于访问PSTATE寄存器中的PAN(Privileged Access Never,特权禁止访问)字段。
UAO寄存器
UAO寄存器用于访问PSTATE寄存器中的UAO(User Access Override,用户访问覆盖)字段。
零寄存器
ARMv8架构提供两个零寄存器(zero register),这些寄存器的内容全是0,可以用作源寄存器,也可以用作目标寄存器。WZR寄存器是32位的零寄存器,XZR是64位的零寄存器。
系统寄存器
除了上面介绍的通用寄存器和特殊寄存器之外,ARMv8架构还定义了很多的系统寄存器,通过访问和设置这些系统寄存器来完成对处理器不同的功能配置。在ARMv7架构里,我们需要通过访问CP15协处理器来间接访问这些系统寄存器,而在ARMv8架构中没有协处理器,可直接访问系统寄存器。ARMv8架构支持如下7类系统寄存器:
- 通用系统控制寄存器
- 调试寄存器
- 性能监控寄存器
- 活动监控寄存器
- 统计扩展寄存器
- RAS寄存器
- 通用定时器寄存器
系统寄存器支持不同的异常等级的访问,通常系统寄存器会使用Reg_ELn的方式来表示:
- Reg_EL1:处理器处于EL1、EL2以及EL3时可以访问该寄存器。
- Reg_EL2:处理器处于EL2和EL3时可以访问该寄存器。
大部分系统寄存器不支持处理器处于EL0时访问,但也有一些例外,如CTR_EL0寄存器。
相关参考
- 《奔跑吧,Linux内核》
- 《ARM64体系结构编程与实践》
- PSTATE.SPSel, SP_EL0的实际使用