AARCH64学习
armv8基础知识
armv8中,执行发生在4个异常级别之一,在aarch64中,异常级别决定了特权级别,类似于armv7中定义的特权级别,异常级别决定特权级别,因此在Eln执行对于特权Pln。类似地,具有比另一个更大的n值的异常级别处于更高的异常级别,一个数字比另一个小的异常级别被描述为处于较低的异常级别。
- EL0 :normal user app
- EL1:Operation system kernel typically described as privileged
- EL2:Hypervisor
- EL3:Low-level firmware,including the Secure Monitor
ARMv8-A提供两种安全状态,安全和非安全,非安全状态也称为正常世界,这使操作系统能够与受信任的操作系统在同一硬件上并行运行,并提供针对某些软件攻击和硬件攻击的保护,ARM TrustZone技术使系统能够在正常和安全世界之间进行分区,与ARMv7-A架构一样,安全监视器充当在正常和安全世界之间移动的网关。
ARMv8-A还提供对虚拟化支持,但仅在普通世界中。这意味着管理程序或虚拟管理器代码可以在系统上运行并托管多个客户操作系统。每个客户操作系统本质上都是在虚拟机上运行。然后,每个操作系统都不会意识到它正在与其他客户操作系统共享系统上的时间。
正常世界(对应于非安全状态)具有以下特权组件:
- Guest Os kernels 此类内核包括在非安全 EL1 中运行的 Linux 或 Windows。在管理程序下运行时,丰富的操作系统内核可以作为来宾或主机运行,具体取决于管理程序型。·
- Hypervisor 这在 EL2 上运行,它始终是非安全的。虚拟机管理程序在存在并启用时,可为丰富的操作系统内核提供虚拟化服务。、
安全世界具有以下特权组件:
Secure frmware 在应用处理器上,这个固件必须是在启动时运行的第一件事。它提供了多种服务,包括平台初始化、可信操作系统的安装以及安全监视器调用的路由。
Trusted Os 受信任的操作系统为普通世界提供安全服务,并为执行安全或受信任的应用程序提供运行时环境。
ARMv8 架构中的安全监视器处于更高的异常级别,并且比所有其他级别具有更高的特权。
这提供了软件特权的逻辑模型。
寄存器
指令集
数据处理指令
一般的格式可以认为是指令,其次是操作数,如下所示。
1 | Instruction Rd, Rn, Operand2 |
第二个操作数可能是一个寄存器,一个修改后的寄存器,或者一个立即数。R的使用表明它可以是一个X或一个W寄存器。
下面的表格显示一些可用的算术和逻辑运算。
种类 | 指令 |
---|---|
算术 | ADD, SUB, ADC, SBC, NEG |
逻辑 | AND, BIC, ORR, ORN, EOR, EON |
比较 | CMP, CMN, TST |
移位 | MOV, MVN |
算术指令示例:
1 | ADD W0, W1, W2, LSL #3 // W0 = W1 + (W2 << 3) |
BIC(位元位清除)指令执行寄存器的AND,这是目标寄存器之后的第一个,具有第二个操作数的倒置值。例如,要清除寄存器X0的位[11],请使用:
1 | MOV X1, #0x800 |
条件指令
处理器状态描述了四个状态标志,零(Z)、负(N)、Carry(C)和溢出(V)。和arm相似
有条件选择(移动)
CSEL 根据一个条件在两个寄存器之间进行选择。无条件指令,然后是条件选择,可以取代简短的条件序列。它的用法是这样的:
1
CSEL <Xd>, <Xn>, <Xm>, <cond>
意思是如果条件(cond)满足,就选择(sel)Xn作为Xd,否则选择Xm作为Xd。比如下面这条语句:
1
CSEL X0, X0, X1, ge
表示的是如果X0>=X1(ge - greater or equal),那么X0=X0(保持不变),否则X0=X1。
CSINC 根据一个条件在两个寄存器之间进行选择。返回第一个源寄存器或第二个源寄存器加一。例如:
1
CSINC X0, X1, X0, NE // Set the return register X0 to X1 if Zero flag clear,else increment X0
上面的代码提供了示例指令的一些别名,其中要么使用零寄存器,要么将同一寄存器用作指令的目标和两个源寄存器。
CSINV 根据条件在两个寄存器之间进行选择。返回第一个源寄存器或倒置的第二个源寄存器。
CSNEG 根据条件在两个寄存器之间进行选择。返回第一个源寄存器或被否定的第二个源寄存器。
这类指令为避免使用分支或有条件执行的指令提供了一个强有力的方法。编译器或汇编程序员可能采用一种技术,对if-then-else语句的两个分支进行操作。然后在最后选择正确的结果。
例如,考虑简单的C代码:
1 | if (i == 0) r = r + 2; else r = r - 1; |
这可能会产生类似于以下内容的代码:
1 | CMP w0, #0 // if (i == 0) |
内存访问指令
加载指令格式
Load指令的一般形式如下:
1 | LDR Rt, <addr> |
对于加载到整数寄存器中,你可以选择一个大小来加载。例如,要加载一个比指定的寄存器值小的尺寸,在LDR指令中加入以下后缀之一:
- LDRB (8-bit, zero extended).
- LDRSB (8-bit, sign extended).
- LDRH (16-bit, zero extended).
- LDRSH (16-bit, sign extended).
- LDRSW (32-bit, sign extended).
存储指令格式:
存储指令的一般形式如下:
1 | STR Rn,<addr> |
偏移寻址模式将一个立即数或一个可选择修改的寄存器值添加到一个64位的基础寄存器中以产生一个地址。
访问多个内存位置
在A64代码中,有加载对(LDP)和存储对(STP)指令。与A32的LDRD和STRD指令不同,任何两个整数寄存器都可以被读取或写入。数据被读入或写入相邻的内存位置。为这些指令所提供的寻址模式选项比其他内存访问指令的限制性更强。LDP和STP指令只能使用一个带有7位有符号即时值的基寄存器,并可选择预增加或后增加。与32位的LDRD和STRD不同,LDP和STP可以进行无符号访问。
无特权访问
A64的LDTR和STTR指令执行无特权的加载或存储(参见ARMv8-A架构参考手册中的LDTR和STTR):
- 在 EL0、EL2 或 EL3,它们表现为正常加载或存储。
- 当在EL1执行时,它们的行为就像是在EL0特权级别执行的一样。
这些指令相当于A32 LDRT和STRT指令。
格式(假设为类似LDR的通用格式):
1 | ldtr{<size>} Rd, <addr> |
<size>
:指定数据的大小,如字节(b)、半字(h)、字(w)等。Rd
:目标寄存器,用于存储从内存中加载的数据。<addr>
:内存地址,指向要加载数据的内存位置。
预取内存
从内存中预取(PRFM)使代码能够向内存系统提供一个提示,即来自特定地址的数据将很快被程序使用。这个提示的效果由实施者决定,但通常情况下,它导致数据或指令被加载到一个缓存中。
指令语法是:
1 | PRFM <prfop>, <addr> | label |
其中prfop是以下选项的串联:
1 | Type PLD or PST (prefetch for load or store). |
例如,PLDL1KEEP。
这些指令与A32 PLD和PLI指令相似。
系统寄存器访问
系统寄存器访问提供了两项说明:
- MRS Xt,
例如:MRS X4, ELR_EL1 // Copies ELR_EL1 to X4
MSR , Xt
例如:MSR SPSR_EL1, X0 // Copies X0 to SPSR_EL1
PSTATE的个别字段也可以用MSR或MRS访问。例如,要选择与EL0相关的堆栈指针或当前的异常级别。
MSR SPSel, #imm // A value of 0 or 1 in this register is used to select // between using EL0 stack pointer or the current exception
// level stack pointer
这些指令有特殊形式可用于清除或设置单个异常掩码位(见第4-5页的保存进程状态寄存器):
- MSR DAIFClr, #imm4
- MSR DAIFSet, #imm4
数据类型
AArch64 内部寄存器在函数调用中的传递标准
通用目的寄存器作为参数
x0 - x7 :参数寄存器,这些寄存器用于将参数传递给函数并返回结果。 它们可以用作临时寄存器或调用者保存的寄存器变量
x8:(间接结果寄存器)用于传递间接结果的地址位置,例如函数返回大型结构的位置(不只是返回,函数内部有大的结构体也会使用)
x9 - x15:(调用者保存临时寄存器)调用者保存的临时寄存器是指在函数调用过程中,其值不由被调用函数(callee)负责保存的寄存器。如果调用者(caller)希望在函数调用后继续使用这些寄存器中的值,它必须在调用之前将这些值保存到其他地方(如堆栈或其他寄存器),并在调用返回后恢复它们。
x16,x17:用作临时寄存器,主要再子程序或函数调用的内部过程中使用。它可以用来存储临时数据,或者作为子程序调用之间值得临时存储。在大多数情况下不需要在函数调用之间保存其内容
x18:(平台寄存器)保留供平台ABI使用。这是平台上得一个附加临时寄存器,无特殊含义
x19 - x29:(被调用者保存寄存器)这些寄存器的值在被调用函数(callee)内部被保存,并在返回给调用者(caller)之前恢复。被调用函数有责任在可能修改这些寄存器的值之前保存它们的原始内容,并在函数返回之前恢复这些值。
x29:( Frame Pointer 寄存器)主要作用是指向当前函数的栈帧(Stack Frame)的起始地址,从而方便访问函数内部的局部变量和参数。在函数开始时,通常会将SP寄存器的值(即当前栈顶的地址)复制到X29寄存器中,以设置FP的值。
x30:(链接寄存器):与arm32类似,保存返回地址
异常处理
如果发生异常,则 PSTATE 信息被保存在 Saved Program Status Register 中。(SPSR_ELn) 存在于 SPSR_EL3、SPSR_EL2 和 SPSR_EL1中