参考资料
ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition
ARM核(ARMv7-A/R)工作模式
-
从异常模式来看,只有 User 和 System 是非异常模式;
-
从特权级别来看,只有 User 是非特权级别;
Privilege Level
ARM Processor Modes
User mode
- 操作系统通过在用户模式下运行引用程序来限制其对系统资源的访问,例如文件、进程等。
- 用户模式下的任何应用程序不能切换模式,除非通过软中断或异常。
System mode
Software executing in System mode executes at PL1. System mode has the same registers available
as User mode, and is not entered by any exception
在系统模式下运行的软件拥有PL1的特权,能够访问一些受控资源。系统模式和用户模式共用同一套可用的寄存器,并且无法通过任何异常来进入该模式。
Supervisor mode
Supervisor mode is the default mode to which a Supervisor Call exception is taken.
Executing a SVC (Supervisor Call) instruction generates an Supervisor Call exception, that is taken
to Supervisor mode.
A processor enters Supervisor mode on Reset.
管理模式是CPU上电后默认的模式,因此在该模式下主要用来做系统的初始化,软中断处理也在该模式下。
当用户模式下的用户程序请求使用硬件资源时,通过软件中断进入该模式。
Abort mode
Abort mode is the default mode to which a Data Abort exception or Prefetch Abort exception is
taken
终止模式是数据终止异常或指令预取终止异常发生时默认进入的模式。
数据终止异常:当用户程序访问非法地址、没有权限读取的地址时会发生该异常。例如linux下编程时经常遇到的segment fault
:
- 引用空指针
- 引用未初始化的野指针
- 引用已经被调用free()函数释放了的悬空指针
- 缓冲区溢出
- 堆栈溢出
- 运行未正确编译的程序(尽管存在编译时错误,某些编译器依然会输出可执行文件)
预取指令异常:预取指令是指令流水线中的概念。该异常发生在预取指令时无法获取到正确的可执行指令,我们有时运行错误代码会出现程序跑飞了的情况就是因为预取不到正确的指令了,CPU无法继续正常执行。
Undefined mode
Undefined mode is the default mode to which an instruction-related exception, including any
attempt to execute an UNDEFINED instruction, is taken
当CPU遇到一个它无法识别的指令时发生该异常。例如在ARM架构的CPU上执行一条Intel指令。
FIQ mode
FIQ mode is the default mode to which an FIQ interrupt is taken.
FIQ是Fast Interrupt reQuetst,快速中断模式。快速是相当于一般中断模式(IRQ)而言的,用来处理对时间要求比较紧急的中断,主要用于高速数据传输及通道处理中。
IRQ mode
一般中断模式,也称普通中断模式,用于处理一般的中断请求。通常在硬件中断发生后自动进入该模式,该模式为特权模式,能够自由访问系统硬件资源。
ARM核寄存器资源
概览
寄存器共享
R0~R7这几个寄存器是所有模式共享的,这意味着在模式发生切换时,为了不影响先前模式的寄存器数据,当前模式需要在使用这些寄存器之前先将它们压栈(内存区域中的栈)保存,并在模式退出时出栈以将寄存器恢复成原样。
同样的R8~R12是除了FIQ的所有模式共享的。
寄存器独有
对于R8~R12,FIQ由自己独有的寄存器,这是为了提高该模式的处理效率:在进入该模式后,访问这几个寄存器无需压栈保护和出栈复原。这也是FIQ比IRQ快的原因之一。
异常模式寄存器
可以发现,对于SP和LR寄存器,除了User和System两个非异常模式,其他模式都对应有自己独立的。它们与异常跳转有关。
PC寄存器
PC(Program Counter)程序计数器用来指向指令地址,在32位CPU顺序执行的过程中,每条指令相隔4个字节,地址相隔0x4,通过将PC的值依次递增0x4就能实现顺序执行的效果。
但是发生模式切换时,需要修改PC的值以告诉CPU从哪里开始接着执行程序指令。
CPSR/SPSR
Current Program Status Register,当前程序状态寄存器,其中包含了当前程序执行过程中产生的一些状态位,例如
- N:negtive,负数标志,例如比较指令
cmp
就是将一个数减去另一个数,如果结果为负,则将N位置1,后续指令可以通过增加条件后缀lt
、le
(less than、less than or equals to)结合N标志位来实现类似if
条件的效果 - C:carry,进位标志
可以理解这个寄存器保存了当前程序的上下文信息。
Saved Program Status Register,暂存的程序状态寄存器。当发生模式切换时,应通过SPSR保存CPSR(之前的程序运行状态),并在返回时将SPSR会写到CPSR。
寄存器用途分析
通用寄存器R0~10
用来存放用户的数据,例如函数入参、函数返回值等
栈相关寄存器
-
R11(fp: frame pointer):用来记录一个栈空间的开始地址
-
R12(ip:The Intra-Procedure-call scratch register)用来临时存储sp
-
R13(sp:stack pointer):栈指针寄存器(指向栈顶),压栈时根据该寄存器中的地址存放数据并更新栈指针到下一个位置
栈空间的计算:sp - fp
跳转相关(branch)寄存器
- R14(lr,link register):在发生跳转(函数调用,中断处理)时,用来保存PC寄存器的值。后面通过将lr会写PC即可实现跳转后的返回。
PC寄存器
program counter:用来存放CPU需要执行的指令地址。
之所以称之为counter计数器,是因为指令宽度是固定的(例如ARMv7中是32位字宽),通过在当前指令的地址上偏移固定的指令位宽(例如4个字节+0x4)就能取到顺序的下一条指令。
CPSR(程序状态寄存器)
条件标志位(可被许多指令设置)
N-负号条件标志位
Negative condition flag. Set to bit[31] of the result of the instruction. If the result is
regarded as a two’s complement signed integer, then the processor sets N to 1 if the result
is negative, and sets N to 0 if it is positive or zero.
当指令的结果为有符号整数时,处理器会在该结果为负数时将N置1。例如我们有时需要根据条件来跳转:
if(a >= b) {
foo();
} else {
fun();
}
对应指令伪代码如下:
mov r0,a // 将a加载到r0寄存器
mov r1,b // 将b加载到r1寄存器
cmp r0,r1 // 通过r0-r1来比较两者的值,如果结果为负数则将N置1,否则置0
bgt foo // b:branch分支跳转 gt:greater than,如果N=0(a>=b),则跳转到foo函数入口地址
ble fun // le:less than or equals to,如果N=1(a<b),则跳转到fun函数入口地址
Z-零条件标志位
Zero condition flag. Set to 1 if the result of the instruction is zero, and to 0 otherwise. A
result of zero often indicates an equal result from a comparison
当指令结果为0时会将Z置1。Z位为1通常标示了一个结果为0的比较指令,例如:
if(a == b) {
foo();
}
对应指令 伪代码如下:
mov r0,a
mov r1,b
cmp r0,r1 // 通过r0-r1来比较两者的值,如果结果为0则将Z置1(当然也会将N置0)
beq foo // b:branch分支跳转 eq:equals to,如果Z=1,则跳转foo函数入口地址
C-进位条件标志位
Carry condition flag. Set to 1 if the instruction results in a carry condition, for example an
unsigned overflow on an addition.
如果指令的结果产生了进位,则C位会被置1。例如加法过程中的无符号溢出。以 uint32_t
为例,最大为 0xFFFFFFFF
,如果对其进行加法操作则会产生进位,例如
uint32_t a = 0xFFFFFFFF;
uint32_t b = a + 1; // 产生进位,C被置1
V-溢出条件标志位
Overflow condition flag. Set to 1 if the instruction results in an overflow condition, for
example a signed overflow on an addition
如果指令的结果产生了溢出(超过了数据类型的表示范围),例如加法中的有符号溢出,则会将该位置1。这里要和C位区分开来,C位是针对无符号的(运算不考虑符号位),V是针对有符号的。例如:
signed char ch = 127;
signed char tmp = ch + 1; // 有符号char最大值为127,加1后为128超过了char的标示范围(-128~127)
I/F-禁用标志位
I位置1标示禁用IRQ异常,F位置1标示禁用FIQ异常
In an implementation that does not include the Security Extensions, setting a mask bit masks the
corresponding exception, meaning it cannot be taken
T标志位
标示了处理器指令集的状态:
- ARM状态:32位指令
- Thumb状态:16位指令(可以理解位ARM指令的压缩版)
M标志位
标示处理器当前的工作模式,参见本文 ARM Processor Modes对应的章节。