type
status
date
slug
summary
tags
category
icon
password
URL
参考文档:Armv8-A Instruction Set Architecture.pdf
一、前言
Armv8-A 中的指令集 Armv8-A 支持三种指令集:A32、T32 和 A64。
在AArch64执行状态下执行时使用A64指令集。它是一个固定长度的32位指令集。名称中的 “64”指的是 AArch64 执行状态对该指令的使用。它不是指内存中指令的大小。
A32 和 T32 指令集也分别称为“ARM”和“Thumb”。这些指令集在 AArch32 执行状态下执行时使用。在本指南中,我们不介绍 A32 和 T32 指令集。
每个版本的 Arm 架构都有自己的 Arm 架构参考手册(Arm ARM),可以在 Arm 开发者网站上找到。每个Arm ARM都提供了每条指令的详细说明,包括:
- 编码——指令在内存中的表示。
- 参数 - 指令的输入。
- 伪代码 - 指令的作用,以 Arm 伪代码语言表示。
- 限制 - 当指令不能使用时,或者它可以触发的异常。
A64 的指令描述也以 XML 和 HTML 形式提供。如果您需要经常参考说明,则 XML 和 HTML 格式
非常有用。 XML 和 HTML 格式可以在 Arm 开发者网站上找到。
1.1 指令概况
指令基本格式
<Opcode>{<Cond>}<S>  <Rd>, <Rn> {,<Opcode2>}
- 其中尖括号是必须的,花括号是可选的
- A32: Rd => {R0–R14}
- A64: Rd =>Xt => {X0–X30}
1.2 指令分类
| 类型 | Note | 
| 跳转指令 | 条件跳转、无条件跳转(#imm、register)指令 | 
| 异常产生指令 | 系统调用类指令(SVC、HVC、SMC) | 
| 系统寄存器指令 | 读写系统寄存器,如 :MRS、MSR指令 可操作PSTATE的位段寄存器 | 
| 数据处理指令 | 包括各种算数运算、逻辑运算、位操作、移位(shift)指令 | 
| load/store内存访问指令 | load/store {批量寄存器、单个寄存器、一对寄存器、非-暂存、非特权、独占}以及load-Acquire、store-Release指令 (A64没有LDM/STM指令) | 
| 协处理指令 | A64没有协处理器指令 | 
1.3 指令助记符
| 整型 | ㅤ | 
| W/R | 32bit整数 | 
| X | 64bit整数 | 
| 加载/存储、符号-0扩展 | ㅤ | 
| B | 无符号8bit字节 | 
| SB | 带符号8bit字节 | 
| H | 无符号16bit半字 | 
| SH | 带符号16bit半字 | 
| W | 无符号32bit字 | 
| SW | 带符号32bit字 | 
| P | Pair(一对) | 
| 寄存器宽度改变 | ㅤ | 
| H | 高位(dst gets top half) | 
| N | 有限位(dst < src) | 
| L | Long (dst > src) | 
| W | Wide (dst==src1,src1>src2) ? | 
1.4 指令条件码
| 编码 | 助记符 | 描述 | 标记 | 
| 0000 | EQ | 运算结果相等为1 | Z==1 | 
| 0001 | NE | 运算结果不等为0 | Z==0 | 
| 0010 | HS/CS | 无符号高或者相同进位,发生进位为1 | C==1 | 
| 0011 | LO/CC | 无符号低清零,发生借位为0 | C==0 | 
| 0100 | MI | 负数为1 | N==1 | 
| 0101 | PL | 非负数0 | N==0 | 
| 0110 | VS | 有符号溢出为1 | V==1 | 
| 0111 | VC | 没用溢出为0 | V==0 | 
| 1000 | HI | 无符号 > | C==1 && Z==0 | 
| 1001 | LS | 无符号 <= | !(C==1 && Z==0) | 
| 1010 | GE | 带符号 >= | N==V | 
| 1011 | LT | 带符号 < | N!=V | 
| 1100 | GT | 带符号 > | Z==0 && N==V | 
| 1101 | LE | 带符号 <= | !( Z==0 && N==V) | 
| 1110 | AL | 无条件执行 | Any | 
| 1111 | NV | ㅤ | ㅤ | 
| ㅤ | ㅤ | ㅤ | ㅤ | 
二、跳转指令
通常,处理器按程序顺序执行指令。这意味着处理器按照指令在内存中设置的顺序执行指令。更改此
顺序的一种方法是使用分支指令。分支指令改变程序流程并用于循环、决策和函数调用。
A64指令集还有一些条件分支指令。这些指令会根据先前指令的结果更改其执行方式。
2.1 无条件跳转指令
2.2 条件跳转指令
2.3 举例说明
第一个例子:如果a等于5,则b等于5
使用汇编编写,如下所示
第二个例子:当a不等于0时,b+c赋值给b,a-1赋值给a
使用汇编
三、PC相对寻址
读取PC的方法:PC相对地址指令(ADR, ADRP),以及branch-and-link指令(BL和BLR)会将PC地址存储在LR寄存器
修改PC的方法:使用显示控制流指令:条件分支、无条件分支、异常产生和异常返回指令
相对于A32的操作PC的MOV指令,A64已经不支持
3.1 ADR指令
使用格式:ADR register exper
编译时,首先会计算出当前PC到exper的偏移量#offset_to_exper
然后会用ADD或者SUB指令,来替换这个指令;例如等效于:ADD register,PC,#offset_to_exper
register就是exper的地址
3.2 ADRP指令
使用格式:ADRP register exper
编译时,会计算出当前PC到exper的偏移量#offset_to_exper
pc的低12位清零,然后加上偏移量给register,得到的地址时含有label的4kb对齐的内存区域的base地址
3.3 实例
- 本代码取自warm项目开机汇编代码的head.S的部分

解释:
- 第365行:读取init_idmap_pd_dir的地址并写入到x0寄存器
- 第366行:读取init_idmap_pd_end的地址并写入x1寄存器
- 第372行:读取init_pd_dir的地址并写入x0寄存器
备注:这部分的地址在vmlinux.lds.S中被定义

四、系统操作指令
4.1 cache操作指令DC/IC
分为DC/IC指令,两者的区别在于DC为操作D-cache的指令,IC为操作I-cache的指令
这部分详看Armv8-A.pdf中的第D4.4.8 A64 Cache maintenance instructions部分
这部分还没有看,到后面专门学cache后来补充这部分内容,暂时设为TODO
4.2 地址翻译指令AT

以AT S1E1R为例

就是说给一个EL1或者EL2权限的虚拟地址,执行此AT指令后,就可以在PAR_EL1寄存器中读到翻译后的地址
下面请看这段c代码



read_sys_reg_par函数其实就是用的MRS指令读取PAR_EL1寄存器
4.3 TLBI指令
TLBI指令用于使 TLBs 中的条目无效。此指令的语法为:
TLBI < type >< level >{IS|OS} {, < xt >}
其中,
< type >,哪些条目无效
All - 所有条目
VA - 匹配在 Xt 的 VA 和 ASID 的条目 [ Entry matching VA and ASID in Xt ]
VAA - 匹配在 Xt 中的 VA ,任何 ASID 的条目
ASID - 匹配在 Xt 中的 ASID 的任何条目
...
< level >,要操作的地址空间
E1 = EL0/1虚拟地址空间
E2 = EL2虚拟地址空间
E3 = EL3虚拟地址空间
< IS|OS >,无论一个操作是内部可共享(IS)还是外部可共享(OS)
当 IS 添加到操作时,它将广播到内部共享域中的其他核心
当 OS 添加到操作时,它将广播到外部共享域的其他核心(在Armv8.4-A中添加)
< Xt >,操作哪个地址或ASID
仅用于按地址或ASID进行的操作
关于TLB的介绍,后面在学习到TLB章节时详细描述,本节仅描述此指令的功能以及用法
以linux内核api flush_tlb_all函数为例


最终调用的指令位tlbi vmalle1is

五、异常产生和返回指令
| 指令 | 解释 | 
| SVC | SVC系统调用,目标异常等级为EL1 | 
| HVC | HVC系统调用,目标异常等级为EL2 | 
| SMC | SMC系统调用,目标异常等级为EL3 | 
| ERET | 异常返回,使用当前的SPSR_ELx和ELR_ELx | 
六、系统存储器指令
| 指令 | 解释 | 
| MRS | R <- S: 通用寄存器 <= 系统寄存器 | 
| MSR | S <- R: 系统寄存器 <= 通用寄存器 | 
七、数据运算指令
| 算数运算 | 逻辑运算 | 数据传输 | 地址生成 | 位段移动 | 移位运算 | 
| ADDS | ANDS | MOV | ADRP | BFM | ASR | 
| SUBS | EOR | MOVZ | ADR | SBFM | LSL | 
| CMP | ORR | MOVK | ㅤ | UBFM | LSR | 
| SBC | MOVI | ㅤ | ㅤ | BFI | ROR | 
| RSB | TST | ㅤ | ㅤ | BFXIL | ㅤ | 
| RSC | ㅤ | ㅤ | ㅤ | SBFIZ | ㅤ | 
| CMN | ㅤ | ㅤ | ㅤ | SBFX | ㅤ | 
| MADD | ㅤ | ㅤ | ㅤ | UBFIZ | ㅤ | 
| MSUB | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | 
| MUL | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | 
| SMADDL | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | 
| SDIV | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | 
| UDIV | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | 
7.1 算术运算指令
| 指令 | 解释 | 
| ADDS | 加法指令,若S存在,则更新条件位flag | 
| ADCS | 带进位的加法,若S存在,则更新条件位flag | 
| SUBS | 减法指令,若S存在,则更新条件位flag | 
| SBC | 将操作数 1 减去操作数 2,再减去 标志位C的取反值 ,结果送到目的寄存器Xt/Wt | 
| RSB | 逆向减法,操作数 2 –操作数 1,结果 Rd | 
| RSC | 带借位的逆向减法指令,将操作数 2 减去操作数 1,再减去 标志位C的取反值 ,结果送目标寄存器Xt/Wt | 
| CMP | 比较相等指令 | 
| CMN | 比较不等指令 | 
| NEG | 取负数运算,NEG X1,X2 // X1 = X2按位取反+1(负数=正数补码+1) | 
| MADD | 乘加运算 | 
| MSUB | 乘减运算 | 
| MUL | 乘法运算 | 
| SMADDL | 有符号乘加运算 | 
| SDIV | 有符号除法运算 | 
| UDIV | 无符号除法运算 | 
7.2 逻辑运算指令
| 指令 | 解释 | 
| ANDS | 按位与运算,如果S存在,则更新条件位标记 | 
| EOR | 按位异或运算 | 
| ORR | 按位或运算 | 
| TST | 例如:TST  W0,  #0X40 //指令用来测试W0[3]是否为1,相当于:ANDS WZR,W0,#0X40 | 
不再多介绍,这部分遇到了直接查
八、load/store指令
| 对齐偏移 | 非对齐偏移 | PC-相对寻址 | 访问一对 | 非暂存 | 非特权 | 独占 | AcquireRelease | 
| LDR | LDUR | LDR | LDP | LDNP | LDTR | LDXR | LDAR | 
| LDRB | LDURB | LDRSW | LDRSW | STNP | LDTRB | LDXRB | LDARB | 
| LDRSB | LDURSB | ㅤ | STP | ㅤ | LDTRSB | LDXRH | LDARH | 
| LDRH | LDURH | ㅤ | ㅤ | ㅤ | LDTRH | LDXP | STLR | 
| LDRSH | LDURSH | ㅤ | ㅤ | ㅤ | LDTRSH | STXR | STLRB | 
| LDRSW | LDURSW | ㅤ | ㅤ | ㅤ | LDTRSW | STXRB | STLRH | 
| STR | STUR | ㅤ | ㅤ | ㅤ | STTR | STXRH | LDAXR | 
| STRB | STURB | ㅤ | ㅤ | ㅤ | STTRB | STXP | LDAXRB | 
| STRH | STURH | ㅤ | ㅤ | ㅤ | STTRH | ㅤ | LDAXRH | 
| ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | LDAXP | 
| ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | STLXR | 
| ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | STLXRB | 
| ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | STLXRH | 
| ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | ㅤ | STLXP | 
8.1 Load/Store (Scaled Offset)
所谓Scaled 和Unscaled其实就是可以见到理解为对齐和非对齐,本质就是是否乘以一个常量,因为scaled的总是可以乘以一个常量来达到对齐,而Unscaled就不需要,是多少就多少,更符合人类自然的理解
| 指令 | 解释 | 
| LDR | 从Memory地址addr中读取双字/字节/半字/字数据到目标寄存器Xt/Wt中带”S”表示需要符号扩展. | 
| LDRB | ㅤ | 
| LDRSB | ㅤ | 
| LDRH | ㅤ | 
| LDRSH | ㅤ | 
| LDRSW | ㅤ | 
| STR | 把Xn/Wn中的双字/字节/半字数据写入到Memory地址addr中 | 
| STRB | ㅤ | 
| STRH | ㅤ | 
8.2 Load/Store (Unscaled Offset)
| 指令 | 解释 | 
| LDUR | 从Memory地址addr中读取双字/字节/半字/字数据到目标寄存器Xt/Wt中带”S”表示需要符号扩展.立即数偏移 #simm9 = { -256 ~ +256 } 的任意整数,不需要对齐规则. | 
| LDURB | ㅤ | 
| LDURSB | ㅤ | 
| LDURH | ㅤ | 
| LDURSH | ㅤ | 
| LDURSW | ㅤ | 
| STUR | 把Xn/Wn中的双字/字节/半字数据写入到Memory地址addr中立即数偏移 #simm9 = { -256 ~ +256 } 的任意整数,不需要对齐规则. | 
| STURB | ㅤ | 
| STURH | ㅤ | 
8.3 Load/Store PC-relative(PC相对寻址)
| 指令 | 解释 | 
| LDR | 从Memory地址addr中读取双字/字数据到目标寄存器Xt/Wt中带”S”表示需要符号扩展. | 
| LDRSW | ㅤ | 
8.4 Load/Store Pair(一对)
| 指令 | 解释 | 
| LDP | 从Memory地址addr处读取两个双字/字数据到目标寄存器Xt1,Xt2带”S”表示需要符号扩展. | 
| LDRSW | ㅤ | 
| STP | 把Xt1,Xt2两个双字/字数据写到Memory地址addr中 | 
8.5 Load/Store Non-temporal(非暂存) Pair
所谓Non-temporal就是就是用于你确定知道该地址只加载一次,不需要触发缓存,避免数据被刷新,优化性能,其它指令都默认会写Cache
| 指令 | 解释 | 
| LDNP | 从Memory地址addr处读取两个双字/字数据到目标寄存器Xt1,Xt2, 标注非暂存访问,不更新cache带”S”表示需要符号扩展. | 
| STNP | 把Xt1,Xt2两个双字/字数据写到Memory地址addr中,标注非暂存访问,不更新cache | 
8.6 Load/Store Unprivileged(非特权)
所谓Unprivileged就是说EL0/EL1的内存有不同的权限控制,这条指令以EL0的权限存取,用于模拟EL0的行为,该指令应用于EL1和EL0之间的交互.
| 指令 | 解释 | 
| LDTR | 从Memory地址addr中读取双字/字节/半字/字数据到目标寄存器Xt/Wt中,当执行在EL1的时候使用EL0的权限带”S”表示需要符号扩展 | 
| LDTRB | ㅤ | 
| LDTRSB | ㅤ | 
| LDTRH | ㅤ | 
| LDTRSH | ㅤ | 
| LDTRSW | ㅤ | 
| STTR | 把Xn/Wn中的双字/字节/半字数据写入到Memory地址addr中,当执行在EL1的时候使用EL0的权限 | 
| STTRB | ㅤ | 
| STTRH | ㅤ | 
8.7 Load/Store Exclusive(独占)
在多核CPU下,对一个地址的访问可能引起冲突,这个指令解决了冲突,保证原子性(所谓原子操作简单理解就是不能被中断的操作),是解决多个CPU访问同一内存地址导致冲突的一种机制。
比如2个CPU同时写,其中一条的Ws就会返回失败值。通常用于锁,比如spinlock,可以参考代码:arch/arm64/include/asm/spinlock.h
| 指令 | 解释 | 
| LDXR | 从Memory地址addr中读取双字/字节/半字数据到目标寄存器Xt/Wt中,标记物理地址是独占访问的 | 
| LDXRB | ㅤ | 
| LDXRH | ㅤ | 
| LDXP | 从Memory地址addr中读取一对双字数据到目标寄存器Xt1,Xt2中,标记物理地址是独占访问的 | 
| STXR | 把Xn/Wn中的双字/字节/半字数据写入到Memory地址addr中,返回是否独占访问成功状态(Ws) | 
| STXRB | ㅤ | 
| STXRH | ㅤ | 
| STXP | 把Xt1,Xt2一对双字字数据写入到Memory地址addr中,返回是否独占访问成功状态 | 
8.8 Load-Acquire/Store-Release
| 指令 | 解释 | 
| Non-exclusive(非独占) | ㅤ | 
| LDAR | 从Memory地址addr中读取一个双字/字节/半字数据到目标寄存器Xt/Wt中,标记物理地址为非独占访问 | 
| LDARB | ㅤ | 
| LDARH | ㅤ | 
| STLR | 把一个双字/字节/半字数据Xt/Wt写到Memory地址addr中,返回是否独占访问成功状态 | 
| STLRB | ㅤ | 
| STLRH | ㅤ | 
| Exclusive(独占) | ㅤ | 
| LDAXR | 从Memory地址addr中读取一个双字/字节/半字数据到目标寄存器Xt/Wt中,标记物理地址为独占访问 LDAXP 是Pair 访问 | 
| LDAXRB | ㅤ | 
| LDAXRH | ㅤ | 
| LDAXP | ㅤ | 
| STLXR | 把一个双字/字节/半字数据Xt/Wt写到Memory地址addr中,返回是否独占访问成功状态 STLXP 是Pair 访问 | 
| STLXRB | ㅤ | 
| STLXRH | ㅤ | 
| STLXP | ㅤ | 
九、内存屏蔽指令
| 指令 | 翻译 | 解释 | 
| DMB | 数据内存屏障指令 | 保证该指令前的所有内存访问结束,而该指令之后引起的内存访问只能在该指令执行结束后开始,其它数据处理指令等可以越过DMB屏障乱序执行 | 
| DSB | 数据同步屏障指令 | DSB比DMB管得更宽,DSB屏障之后的所有得指令不可越过屏障乱序执行 | 
| ISB | 指令同步屏障指令 | ISB比DSB管的更宽,ISB屏障之前的指令保证执行完,屏障之后的指令直接flush掉再重新从Memroy中取指 | 
在第4.2 地址翻译指令AT的案例中也使用了dsb的内存屏蔽指令