arm64架构分析
此文分析基于arm官方文档
ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile
ARM Architecture Reference Manual Supplement ARMv8.1, for ARMv8-A architecture profile Errata markup
指令集
1. A64 64位ARM指令集,这里64位指数据处理能力即一条指令处理的数据宽度,指令编码使用定长32比特编码
2. A32 32位ARM指令集,这里32位指数据处理能力即一条指令处理的数据宽度,指令编码使用定长32比特编码
3. T32 thumb指令集,32位指令(即一条指令可以处理32比特到数据),指令编码为16比特32比特混合编码(具有更高的指令密度,16比特指令可以访问的寄存器数受限,性能会低很多)
寄存器
arm64有31通用寄存器X0-X30,可以作为32或64比特的寄存器,32比特时记为W0-W30,64比特时记为X0-X30。
指令使用5比特编码寄存器。当寄存器编号为31时,使用零寄存器(zero register)。零寄存器非物理寄存器,32比特时记作WZR,64比特时记作XZR。
LR为X30。LR(链接寄存器)在函数调用指令(BL、BLR)中使用,用于保存返回地址
SP(stack point)、PC(program counter)、CPSR(current program state register)为特殊功能寄存器,通过特殊指令访问,在指令中没有寄存器编号。
SP(stack point)
- 在ARMv8有4个SP指针,SP0、SP_EL1、SP_EL2、SP_EL3
- EL0只能使用SP0
- ELx(x > 0)可以使用SP0或者使用SP_ELx(即在EL1下可以使用SP0、SP_EL1,在EL2下可以使用SP0、SP_EL2)
CPSR(current program state register)非物理寄存器,是一组寄存器(NZCV/DAIF/CurrentEL/SPSel)的映射,不能通过指令直接访问。通过访问NZCV/DAIF/CurrentEL/SPSel寄存器间接访问CPSR
- N , bit[31] 标记计算结果为负
- Z , bit[30] 标记计算结果为零
- C , bit[29] 标记无符号数计算进位
- V , bit[28] 标记有符号数计算益处
- Q , bit[27] ???
- J , bit[24]
- T , bit[5]
- T和J配合用于确定32 位执行模式下的状态
- {0,0} ARM状态
- Thumb状态,执行thumb2指令
- Thumb状态,执行thumbEE指令
- ARM 32下指令集切换
- ARM和thumb状态切换,执行BL、BLX
- thumb和thumbEE状态切换,执行ENTERX、LEAVEX
- {0,0} ARM状态
- T和J配合用于确定32 位执行模式下的状态
- PAN,bit[22] 权限相关
- GE ,bit[19:16] 并行加减法大于等于标志位
- IT ,bit[15:10] bit[25:26]
- thumb32 IT(if-then)指令相关
- E ,bit[9] 大小端状态位
- A ,bit[8] 异步数据终止异常标记位
- I ,bit[7] IRQ异常标记位
- F ,bit[6] FIQ异常标记位
- M ,bit[4:0] 处理器模式位,64和32位模式下意义不同
- M[4]
- 1 执行状态是ARRCH32
- 0 执行状态是ARRCH64
- M[3:0] AARCH32标记异常模式
- 0b0000 User
- 0b0001 FIQ
- 0b0010 IRQ
- 0b0011 Supervisor
- 0b0110 Monitor
- 0b0111 Abort
- 0b1010 Hyp
- 0b1011 Undefined
- 0b1111 System
- M[3:0] AARCH64下分成多段
- M[3:2] 异常等级
- M[1] 保留未用
- M[0] SP选择
- 0 使用SP_EL0
- 使用SP_ELx
- M[4]
异常等级(Exception Level)
异常等级是ARMv8引入到权限等级,ARMv8有4级异常等级EL0、EL1、EL2、EL3数值越大权限越大,具体芯片可以选择实现EL3、EL2
通常
EL0用于User App
EL1用于OS
EL2用于Hypervisor
EL3用于Secure monitor
执行状态(Execution State)
ARMv8有两种执行状态:
-
AArch64用于执行64位指令集
-
AArch32用于执行32位指令集
执行状态的控制
-
如果高异常等级执行在AARCH32状态下,低的异常等级必须执行在AARCH32状态下
-
EL3执行状态
- AARCH64->AARCH32
- RMR_EL3.AA64写0
- AARCH32->AARCH64
- RMR.AA64写1
- 在AARCH32状态下RMR是CP15协处理器的寄存器,通过以下指令访问
MRC P15, 0, <Rt>, C12, C0, 2
; Read RMR into RtMCR P15, 0, <Rt>, C12, C0, 2
; Write Rt to RMR
- RMR是RMR_EL3在32模式下的映射,写此寄存器将触发热复位
- AARCH64->AARCH32
-
EL2执行状态
- SCR_EL3.RW,如果为0低异常等级(EL0/EL1/EL2)只能执行AARCH32,如果为1低异常等级执行状态由CPSR.M[4]决定0->64bit、1->32bit
-
EL1执行状态
- HCR_EL2.RW,如果为0低异常等级(EL0/EL1)只能执行AARCH32,如果为1低异常等级执行状态由CPSR.M[4]决定0->64bit、1->32bit
- CPSR.M[4]不能直接修改,只能通过异常进入高异常等级修改SPSR.M[4]来间接修改
安全状态(Security state)
-
Non-secure(EL0、EL1、EL2) 只能访问Non-secure memory
-
secure(EL0、EL1、EL3) 可以访问secure & Non-secure memory
- EL3只能在secure state
- EL2只能在Non-secure
- EL1&0安全状态由SCR_EL3.NS位决定,1->Non-secure,0->secure
异常路由(发生异常时跳转到ELx)
- SCR_EL3.EA、SCR_EL3.IRQ、SCR_EL3.FIQ,如果为1,底异常等级发生的异常都路由到EL3
- HCR_EL2.AMO、HCR_EL2.IMO、HCR_EL2.FMO,如果为1,底异常等级Non-secure下的异常都路由到EL2
- EL0不处理异常,以上比特位都为0,EL0的异常都路由到EL1
异常返回
- 异常返回有特有的返回指令ERET
- 异常有独立的链接寄存器ELR_ELx(保存返回地址)
- 通过修改SPSR_ELx.M[3:2]可以确定异常返回后到异常等级
- 通过修改SPSR_ELx.M[4]可以修改异常返回后的执行状态(ARRCH64/ARRCH32)
异常向量
-
EL0不能处理异常,所以EL0没有异常向量
-
每一个异常等级有一个寄存器用于设置异常的起始地址,Vector Base Address Register(VBAR_ELx)
-
根据异常得来源异常向量被分为4段(当前异常等级并使用SP_EL0,当前异常等级并使用SP_ELx,低异常等级64位模式,低异常等级32位模式)
-
每一段有4中异常Synchronous exception/SError/IRQ/FIQ
异常地址如下:
异常来源 | Synchronous | IRQ | FIQ | SError |
---|---|---|---|---|
当前异常等级并使用SP_EL0 | 0x000 | 0x080 | 0x100 | 0x180 |
当前异常等级并使用SP_ELx | 0x200 | 0x280 | 0x300 | 0x380 |
低异常等级AARCH64 | 0x400 | 0x480 | 0x500 | 0x580 |
低异常等级AARCH32 | 0x600 | 0x680 | 0x700 | 0x780 |
内存管理
基本概念
- 物理地址(PA),芯片地址线上使用的实际地址
- 虚拟地址(VA),程序可见的地址空间
- 输入地址(IA),地址转换之前的输入地址
- 输出地址(OA),地址转换之后的输出地址
- 中间物理地址(IPA),ARM64支持两级地址转换,当使用两级地址转换时,第一级地址转换输出的地址会用于第二级地址转换的输入,两级转换的中间地址就叫做IPA
- 页,MMU可以管理的最小内存块
- 页表描述符,用于描述虚拟地址和物理地址转换关系,以及内存属性
- 页表,页表描述符的表
ARMv8内存管理
-
ARMv8支持4k/16k/64k三种类型的页
-
ARMv8最大支持48位的物理地址空间(即支持256T的物理内存)
-
ARMv8支持48位的虚拟地址空间(即支持256T的虚拟地址空间)
-
ARMv8有两级地址转换单元(stage1/stage2)
- EL3 Secure 使用stage1转换
- EL2 Non-Secure 使用stage1转换
- EL0&1 Secure 使用stage1转换
- EL0&1 Non-Secure 使用stage1&2转换(只有这中情况下有两级地址转换,才会存在IPA)
地址转换规则
-
ARMv8支持多级页表,即通过页表描述符找到的地址是下一级页表的地址,需要多次转换才能找到最终的地址
-
ARMv8把输入地址分为多个段(每个段分别作为各级页表的偏移量,或者目标页的偏移量)
-
4k页时虚拟地址分段规则
+--------+--------+--------+--------+--------+--------+--------+--------+
|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0|
+--------+--------+--------+--------+--------+--------+--------+--------+
| | | | | |
| | | | | v
| | | | | [11:0] in-page offset
| | | | +-> [20:12] L3 index
| | | +-----------> [29:21] L2 index
| | +---------------------> [38:30] L1 index
| +-------------------------------> [47:39] L0 index
+-------------------------------------------------> [63] TTBR0/1
- 16k页时虚拟地址分段规则
+--------+--------+--------+--------+--------+--------+--------+--------+
|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0|
+--------+--------+--------+--------+--------+--------+--------+--------+
| || | | |
| || | | v
| || | | [13:0] in-page offset
| || | +------->[24:14] L3 index
| || +------------------->[35:25] L2 index
| |+------------------------------->[46:36] L1 index
| +-------------------------------->[47] L0 index
+-------------------------------------------------->[63] TTBR0/1
- 64k页时虚拟地址分段规则
+--------+--------+--------+--------+--------+--------+--------+--------+
|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0|
+--------+--------+--------+--------+--------+--------+--------+--------+
| | | | |
| | | | v
| | | | [15:0] in-page offset
| | | +----------> [28:16] L3 index
| | +--------------------------> [41:29] L2 index
| +-------------------------------> [47:42] L1 index
+-------------------------------------------------> [63] TTBR0/1
页表描述符
页表描述符64位,地址8字节对齐,所以在查找下一级页表时低3比特全部为0。页表描述符在表示目标页地址时低12(4k)/14(16k)/16(64k)位都为0。所以在页表描述符中,低3比特肯定是无效位。
ARMv8利用这个特性使用了其中低两个比特位,用于标识页表描述符的类型
-
L0/L1/L2时
- bit[0]标识页表描述符是否有效(1有效,无效将触发却页中断)
- bit[1]标识页表描述符有没有下一级页表(1有下一级页表,没有下一级页表时把没有使用的地址都作为地址偏移量)
-
L3时
- bit[0]标识页表描述符是否有效(1有效,无效将触发却页中断)
- bit[1]标识页表描述符是不是页地址(1是页地址)
页表描述符[47:3]比特记录地址信息,根据每一级页表偏移的位数或页偏移的位数把低位清0,这样只需要或上偏移量就可以算出目标地址
- 所以,页地址需要4k/16k/64k地址对齐
- 所以,页表描述符表也需要根据具体情况对齐,列如4k的页表描述符需要2k(512*64bit)对齐(页表偏移量都为9位)页表描述符,高16位无效(64-48),ARMv8把这一部分用于记录权限信息(只在stage1转换期间有效,在stage2转换期间保留为0)
页表描述符,高16位无效(64-48),ARMv8把这一部分用于记录权限信息(只在stage1转换期间有效,在stage2转换期间保留为0)
- NSTable,[63] 用于标识描述符描述的内存是否为Non-Secure Memory(0 Non-Secure memory,1 Secure memory)
- APTable,[62:61]
- 01 标识目标内存EL0没有权限访问
- 10 标识目标内存不能写(EL0/EL1/EL2/EL3)
- 11 标识目标内存EL0不能访问,并且目标内存不能写(EL0/EL1/EL2/EL3)
- XNTable,[60] 标识目标内存能否执行(1不能执行)
- PXNTable,[59] 标识目标内存能否执行(1不能执行),标识特权级可执行PXNTable,[59] 标识目标内存能否执行(1不能执行),标识特权级可执行
缓存
缓存(cache)一般由tag data controller组成。tag用于记录下一级内存中那一部分被缓存了,data用于保存缓存的内容,controller用于控制缓存的工作。
基本概念
- 缓存页(cache page)缓存与下一级存储器交换的最小单位
- 缓存行(cache line)缓存页内部又被分成多个缓存行
- 脏的缓存(dirty)与下一级缓存不一致的缓存
- 干净的缓存(clean)与下一级缓存一致的缓存
缓存分类
缓存可以分为三类
全关联缓存(fully-associative)
这种缓存不使用page,直接通过line与下一级存储器交换数据,它的page容量等于line的容量
具有速度快、缓存粒度小、容量小、电路复杂的特征
直接映射(direct map)
这种缓存只有一个页。
具有速度慢、缓存粒度大、容量大、电路简单的特征
组关联(set-associative)
这种缓存具有多个缓存页。
这种缓存是性能和成本介于上面两种缓存中一种缓存
缓存策略(Cache policies)
缓存策略指何时向下一级缓存亲求数据以及何时把数据写入到下一级缓存
Write Allocation(WA)
写操作不命中时进行缓存操作
Read Allocation(RA)
读操作不命中时进行缓存操作
Write-back (WB)
在缓存失效时,把脏的缓存写入下一级存储器
Write-through (WT)
写的数据不缓存,直接写入到下一级存储器
内存一致性
在SOC内部有多个主控设备(CPU、MMU、DSP等),按些主控观察到的相同的内存点分类,ARM把内存一致性分为两类POC、POU
POC(Point of Coherency)
POC指所有主控看到内存一致的存储器。一般为主存储器。
POU
指一个主控看到的内存一致的存储器。ARM一般把一级缓存分为数据缓存和指令缓存,所以一般的ARM芯片的POU指二级缓存,在没有二级缓存的芯片上就为主存储器。