ARMv7-A 处理器窥探 --- 处理器模式
arm官方资料
ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition.pdf
特权等级(Privilege level)
ARMv7架构支持安全扩展,如果使能了安全扩展,ARMv7-A架构分为安全模式(Secure State)和非安全模式(Non-secure State)两个世界。
在非安全模式下,存在三种运行特权 PL0,PL1和 PL2(Privilege level)。(这里仅仅讨论非安全 State)
特权等级 | 描述 |
PL0 | PL0运行在用户模式(User),用于运行应用程序。该模式程序受限访问系统资源。对应Linux用户态。 |
PL1 | PL1运行除用户模式和Hyp模式外的所有模式。Linux内核运行在PL1。包含了ARMv7架构中的System,SVC,FIQ,IRQ,UNDEF及Abort模式。此外,安全模式中的Montior也运行在PL1等级,管理安全模式和非安全模式的切换。 |
PL2 | PL2用于虚拟化。虚拟化超级管理程序(Hypervisor)运行在 PL2。 |
这里的 PL0~PL2 指的是特权等级,不同的特权等级,访问资源的权限不一样,操作系统运行在 PL1 的特权等级下,具有较高的访问权限,用户态运行再 PL0 的特权等级下(也叫非特权等级),只有最最基本的访问权限;
处理器模式(mode)
上面说的是处理器的特权等级,那么处理器真正的运行的模式有几种呢?如下所示:
可以看到,ARMv7-A 的处理器模式有 User、FIQ、IRQ、Supervisor、Monitor、Abort、Hyp、Undefined、System 模式:
- User:用户模式,运行再 PL0 这个特权等级上,也就是没有特权等级,他是OS上运行应用程序时候的等级,他不可以访问系统资源(诸如 MMU 等),在这个模式下,无法主动切换模式,除非遇到中断或者异常
- FIQ:快中断模式,发生 FIQ 快中断的时候处理器模式;
- IRQ:中断模式,发生 IRQ 中断的时候处理器模式;
- Supervisor:管理员模式,复位后的默认模式,运行在 PL1 特权等级,可以执行 SVC 指令产生 Supervisor Call 异常,进入 Supervisor 模式,操作系统常用的模式;
- Monitor:监视模式,针对 Security 扩展,不详细讨论;
- Abort:停止模式,当发生 Data Abort exception 或者 Prefetch Abort exception 异常时候进入这个模式;
- Hyp:当支持虚拟化扩展的时候模式,不详细讨论;
- Undefined:这是执行和指令相关的模式,当企图执行 UNDEFINED 指令的时候进入这个模式;
- System:系统模式,也是 PL1 特权等级,和 Supervisor 的区别是,System 模式具有和 User 模式一样的寄存器,目前大多数系统未使用;
异常处理(Exception handling)
产生异常后,暂停程序的运行,跳转到异常处理函数执行,执行完后再回到异常产生的程序继续执行。
下图是异常模式产生后,会进入的处理器模式,比如复位后会进入Supervisor模式,执行SVC指令(系统调用)也会进入Supervisor模式
注:异常模式的名字和处理器模式的名字大多是一样的,但是不是一一对应的关系。
下图是各种异常处理结束后首选的返回地址,对于SVC(系统调用),会返回到执行 SVC 指令的下一个指令地址。
核寄存器
通用寄存器
ARMv7-A 处理器有 16 个通用寄存器:R0~R15,其中:
- R13:通常用做堆栈指针 SP;
- R14:通常用作链接寄存器 LR;
- R15:通常用作程序计数器 PC;
前面说了处理器有特权等级,每种特权等级访问系统资源的权限不一样,而处理器又有几种模式,每种模式对应的特权等级有一定区别;
每一种处理器模式对应的寄存器也有一定区别:
从上图可以看出:
- 1、R0~R7,PC是所有模式下共享的;
- 2、FIQ 模式下,R8~R12、SP、LR 都是有专门的寄存器,有的材料上,称之为“影子寄存器”,什么意思呢?这个模式下,有他专用的 R8~R12、SP、LR;
- 3、同样道理,Supervisor、Abort、Undefined、IRQ 等,都有他们自己模式下专用的 SP 和 LR,也就是说,从其他模式进来的时候,不需要针对这两个寄存器进行恢复现场;
- 4、FIQ 之所以称之为 FIQ,从软件上也看得出来,他专用的寄存器要多于 IRQ 的,所以也的确是要 Fast 一些;
从这里,我们也可以看出,exception 发生的时候,我们其实是有必要手动保存一些现场的;
特殊寄存器
ARMv7-A 还有一个特殊寄存器叫:程序状态寄存器 CPSR(Current Program Status Register),在进入异常之前,当前的 CPSR 被保存到 SPSR (Saved Program Status Register)中;
当然 CPRS 在用户层叫做 APSR,APSR 只是 CPSR 寄存器中被截取的一部分,因为在用户层,并不是所有的 CPSR 的位都可以访问;
CPSR 的组成如下所示:
这里的 M[4:0] 就是直接对应到了前面讲到的模式,还记得在前面那个处理器模式的列表中,每一个模式都对应了一个 Encoding 吗?这个 Encoding 就是这个 M 位的值;
指令集
ARMv7-A 支持 32bit ARM 指令集的同时,还支持 16bit 的 Thumb 指令集,它具有更好的代码密度,处理器可以在这两种指令集之间切换;
所有的Cortex-A系列处理器实现了Thumb-2技术,它扩展了Thumb指令集。混合使用32位和16位指令,以Thumb指令集的代码密度和接近ARM指令集的性能。自从所有的Cortex-A系列处理器支持这一扩展,针对它们的软件常被编译成Thumb指令集;
ARM 是加载/存储体系结构的典型的RISC处理器,对存储器的访问只能使用加载和存储指令实现。ARM 的加载/存储指令是可以实现字、半字、无符/有符字节操作;批量加载/存储指令可实现一条指令加载/存储多个寄存器的内容,大大提高效率;
指令集格式
基本格式为:
<opcode>{<cond>}{S} <Rd>,<Rn>{,<opcode2>}
<>内的项是必须的,{}内的项是可选的,如<opcode>是指令助记符,是必须的,而{<cond>}为指令执行条件,是可选的,如果不写则使用默认条件AL(无条件执行)
- opcode:指令助记符,如 LDR,STR 等
- cond:执行条件,如EQ,NE 等
- S:是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响
- Rd :目标寄存器
- Rn:第一个操作数的寄存器
- operand2:第二个操作数
常用指令集
LDR/STR
LDR指令用于从内存中读取数据放入寄存器中;STR 指令用于将寄存器中的数据保存到内存。指令格式如下:
LDR{cond}{T} Rd,<地址>; 加载指定地址上的数据(字),放入Rd中
STR{cond}{T} Rd,<地址>; 存储数据(字)到指定地址的存储单元,要存储的数据在Rd中
LDR/STR 指令寻址是非常灵活的,由两部分组成,一部分为一个基址寄存器,可以为任一个通用寄存器,另一部分为一个地址偏移量。地址偏移量有以下3种格式:
(1) 立即数。立即数可以是一个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R0,=0X123 ; 将0X123存入r0中 LDR R0,=label ; 将label_1所指向的地址值存入r0中 LDR R1,[R0] ; 将 R0 地址处的数据读出,保存到R1中 (R0 的值不变) LDR R1,[R0,#0x12] ; 将 R0+0x12 地址处的数据读出,保存到R1中 (R0 的值不变) LDR R1,[R0,#-0x12]; 将 R0-0x12 地址处的数据读出,保存到R1中 (R0 的值不变)
(2)寄存器。寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例值。指令举例如下:
LDR R1,[R0,R2] ; 将R0+R2 地址的数据计读出,保存到R1中(R0 的值不变)
LDR R1,[R0,-R2] ; 将R0-R2 地址的数据计读出,保存到R1中(R0 的值不变)
(3)寄存器及移位常数。寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R1,[R0,R2,LSL #2] ;将R0+R2*4地址处的数据读出,保存到R1中(R0,R2的值不变) LDR R1,[R0,-R2,LSL #2];将R0-R2*4地址处的数据计读出,保存到R1中(R0,R2的值不变)
一组代码示例:
NumCount EQU 0x40003000 ;定义变量NumCount … LDR R0,=NumCount ;使用LDR 伪指令装载NumCount的地址到R0 LDR R1,[R0] ;取出变量值 ADD R1,R1,#1 ;NumCount=NumCount+1 STR R1,[R0] ;保存变量值 … GPIO 设置 GPIO-BASE EQU 0Xe0028000 ;定义GPIO 寄存器的基地址 … LDR R0,=GPIO-BASE LDR R1,=0x00FFFF00 ;装载32 位立即数,即设置值 STR R1,[R0,#0x0C] ;IODIR=0x00FFFF00, IODIR 的地址为0xE002800C MOV R1,#0x00F00000 STR R1,[R0,#0x04] ;IOSET=0x00F00000,IOSET 的地址为0xE0028004
LDM/STM
批量加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM 为加载多个寄存器,STM 为存储多个寄存器。允许一条指令传送16 个寄存器的任何子集或所有寄存器。指令格式如下:
LDM{cond}<模式> Rn{!},reglist{^}
STM{cond}<模式> Rn{!},reglist{^}
LDM /STM 的主要用途是现场保护、数据复制、参数传送等。其模式有8种,如下所列:(前面4 种用于数据块的传输,后面4 种是堆栈操作)。
(1) IA:每次传送后地址加4 (2) IB:每次传送前地址加4 (3) DA:每次传送后地址减4 (4) DB:每次传送前地址减4 (5) FD:满递减堆栈 (6) ED:空递增堆栈 (7) FA:满递增堆栈 (8) EA:空递增堆栈
其中,寄存器Rn 为基址寄存器,装有传送数据的初始地址,Rn 不允许为R15;后缀“!”表示最后的地址写回到Rn中;寄存器列表reglist 可包含多于一个寄存器或寄存器范围,使用“,”分开,如{R1,R2,R6-R9},寄存器排列由小到大排列;“^”后缀不允许在用户模式呈系统模式下使用,若在LDM 指令用寄存器列表中包含有PC 时使用,那么除了正常的多寄存器传送外,将SPSR 拷贝到CPSR 中,这可用于异常处理返回;使用“^”后缀进行数据传送且寄存器列表不包含PC时,加载/存储的是用户模式的寄存器,而不是当前模式的寄存器。
地址对准――这些指令忽略地址的位[1:0]。
批量加载/存储指令举例如下:
LDMIA R0!,{R3-R9} ;加载R0 指向的地址上的多字数据,保存到R3~R9中,R0 值更新 STMIA R1!,{R3-R9} ;将R3~R9 的数据存储到R1 指向的地址上,R1值更新 STMFD SP!,{R0-R7,LR} ;现场保存,将R0~R7、LR入栈 LDMFD SP!,{R0-R7,PC}^;恢复现场,异常处理返回
在进行数据复制时,先设置好源数据指针,然后使用块拷贝寻址指令LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB/STMDB 进行读取和存储。而进行堆栈操作时,则要先设置堆栈指针,一般使用SP 然后使用堆栈寻址指令STMFD/LDMFD、STMED。LDMED、STMFA/LDMFA、STMEA/LDMEA实现堆栈操作。
使用LDM/STM 进行数据复制例程如下:
… LDR R0,=SrcData ;设置源数据地址 LDR R1,=DstData ;设置目标地址 LDMIA R0,{R2-R9} ;加载8 字数据到寄存器R2~R9 STMIA R1,{R2-R9} ;存储寄存器R2~R9 到目标地址 使用LDM/STM 进行现场寄存器保护,常在子程序中或异常处理使用: SENDBYTE STMFD SP!,{R0-R7,LR} ;寄存器入堆 … BL DELAY ;调用DELAY 子程序 … LDMFD SP!,{R0-R7,PC} ;恢复寄存器,并返回
值得注意的是一些诸如原子操作的指令:STREX/LDREX;
MRS/MSR
特殊寄存器 CPSR 通过 MRS 和 MSR 指令进行读写操作:
MRS:读状态寄存器指令。在ARM 处理器中,只有 MRS 指令可以状态寄存器CPSR或SPSR读出到通用寄存器中。
MRS{cond} Rd ,psr
Rd 目标寄存器。Rd 不允许为R15
举例:
MRS R1,CPSR ;将CPSR状态寄存器读取,保存到R1 中
MRS R2,SPSR ;将SPSR状态寄存器读取,保存到R2 中
MRS 指令读取CPSR,可用来判断ALU 的状态标志,或IRQ、FIQ中断是否允许等;在异常处理程序中,读SPSR 可知道进行异常前的处理器状态等。MRS 与MSR 配合使用,实现CPSR 或SPSR 寄存器的读—修改---写操作,可用来进行处理器模式切换(),允许/禁止IRQ/FIQ中断等设置。另外,进程切换或允许异常中断嵌套时,也需要使用MRS 指令读取SPSR 状态值。保存起来
举例:
使能IRQ 中断例程: ENABLE_IRQ MRS R0,CPSR BIC R0,R0,#0x80 MSR CPSR_c,R0 MOV PC,LR 禁能IRQ 中断例程: DISABLE_IRQ MRS R0,CPSR ORR R0,R0,#0x80 MSR CPSR_c,R0 MOV PC,LR
MSR:写状态寄存器指令。在ARM 处理器中。只有MSR 指令可以直接设置状态寄存器CPSR或SPSR。指令格式如下:
MSR{cond} psr_fields,#immed_8r
MSR{cond} psr_fields,Rm
psr CPSR 或 SPSR,
fields 指定传送的区域。Fields 可以是以下的一种或多种(字母必须为小写):
c 控制域屏蔽字节(psr[7…0])
x 扩展域屏蔽字节(psr[15…8])
s 状态域屏蔽字节(psr[23。…16])
f 标志域屏蔽字节(psr[31…24])
immed_8r 要传送到状态寄存器指定域的立即数,8 位。
Rm 要传送到状态寄存器指定域的数据的源寄存器。
MSR 指令举例如下:
MSR CPSR_c,#0xD3 ;CPSR[7…0]=0xD3,即切换到管理模式。 MSR CPSR_cxsf,R3 ;CPSR=R3
注意:只有在特权模式下才能修改状态寄存器!
程序中不能通过MSR 指令直接修改CPSR 中的 T 控制位来实现ARM 状态/Thumb状态的切换,必须使用 BX 指令完成处理器状态的切换(因为BX 指令属转移指令,它会打断流水线状态,实现处理器状态切换)。MRS 与MSR 配合使用,实现CPSR或SPSR 寄存器的读-修改-写操作,可用来进行处理器模式切换、允许/禁止IRQ/FIQ 中断等设置。
堆栈指令实始化例程: INITSTACK MOV R0,LR ;保存返回地址 ;设置管理模式堆栈 MSR CPSR_c,#0xD3 LDR SP,StackSvc ;设置中断模式堆栈 MSR CPSR_c,#0xD2 LDR SP,StackIrq