ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器

/* TODO 本系列文章是对 ARMv8 Cortex-a 系列编程向导手册拙劣的翻译和注解,若有出入,以官方文档为准 */

Chapter 4 ARMv8 寄存器

AArch64 执行状态提供了 31 个 64 位宽的通用目的寄存器,可以在所有异常等级访问,每一个异常等级任一寄存器的访问都是相同的,这 31 个寄存器被称为 X0 - X30.
ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器_第1张图片
其中 W0-W30 是 X0 - X30 的 32 位格式,对应低32位(X30 保存子函数的返回地址?ELR_ELn 保存异常返回地址)。
如果写 W0 寄存器,那么 X0 寄存器的高 32 位会置 0.

4.1 AArch64 特殊寄存器

除了 31 个内核通用寄存器,ARMv8 也提供了一些特殊寄存器。

寄存器名 寄存器描述
WZR 32 位宽的 0 寄存器,读寄存器的值恒为 0
XZR 64 位宽的 0 寄存器,同上
SP_ELn 堆栈指针,每一个异常等级都有专用的堆栈指针,n 表示异常等级
ELR_ELn 保存异常返回地址的寄存器,当执行 ERET 指令时,该寄存器为异常返回后的执行地址,n 取值范围1-3,因为没有异常会在 EL0 中处理
SPSR_ELn 用于保存异常发生时的处理器状态的寄存器,该寄存器备份的是 PSTATE 各位域的值,当执行 ERET 指令时,该寄存器会设置 PSTATE 各位域,n 取值范围1-3,因为没有异常会在 EL0 中处理

4.1.1 WZR/XZR 0寄存器

当该寄存器用作源寄存器时,读出的数据是0,当该寄存器用作目的寄存器时,写入的数据被丢弃。

4.1.2 SP_ELn 堆栈指针寄存器

在ARMv8架构中,当异常发生时,通常会伴随堆栈指针的切换,每一个异常等级一般都会使用该异常等级专用的寄存器,但除了 EL0 之外的异常等级,这些特权异常等级所使用的堆栈指针是可选的:

  1. 特权异常等级 EL1-3 默认使用该特权等级专属的堆栈指针寄存器 SP_ELn.
  2. 特权异常等级 EL1-3 可以选择使用 SP_EL0.
  3. EL0 只能使用 SP_EL0.

AArch64 堆栈指针选择如下:
ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器_第2张图片
其中 t 后缀表示选择 SP_EL0 堆栈指针,h 后缀表示选择 SP_ELn 堆栈指针(目前 t 与 h 的应用不懂,t 与 h 可以不用尝试理解)。
大多数指令不能直接使用 SP 指针,但是一些算数指令可以使用 SP 指针,比如 ADD 指令:
ADD SP, SP, #0x10 // Adjust SP to be 0x10 bytes before its current value

4.1.3 PC 程序计数器

在 ARMv7 架构中, PC 寄存器可以做为一个通用寄存器使用。在 ARMv8 架构中,移除了对 PC 寄存器的直接访问,这可以让程序流更加可预测。

在 ARMv8 架构中, PC 不像任何一个已命名的寄存器那样可访问,而是使用隐形的 PC 相关的加载和地址生成指令(比如 B,BL,BLR). PC 绝不能做为一个数据处理指令和加载指令的目的寄存器。

4.1.4 ELR_ELn 异常链接寄存器

该寄存器保存了异常返回地址,当执行 ERET 指令从异常中返回时,该寄存器的值会赋值给 PC

4.1.5 SPSR_ELn 备份程序状态寄存器

当异常发生时,处理器状态被保存到 SPSR_ELn 寄存器中(类似 ARMv7 的 CPSR 寄存器)。SPSR_ELn 寄存器保存异常发生时 PSTATE 的值,当异常返回时,SPSR_ELn 寄存器用于恢复 PSTATE 的值(PSTATE 用于保存处理器状态,这不是一个寄存器,更像是一块专用内存,该内存的各位域可单独访问)。
SPSR_ELn 寄存器结构如下:
ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器_第3张图片

位域 描述
N 负值标志位
Z 0值标志位
C 进位标志位
V 溢出标志位
SS software step
IL 非法执行状态位
D 调用屏蔽位
A SError(System error ) 中断屏蔽位
I IRQ 中断屏蔽位
F FIQ 中断屏蔽位
M 异常发生时的执行状态指示位,0 表示异常发生时的执行状态为 AArch64 (注意,不通过该位修改执行状态)
M[3:0] 异常发生时的处理器模式或处理器异常等级
在 ARMv8 架构中,SPSR 寄存器的选择依据异常等级(类似 ELR 寄存器)。当异常在 EL1 中处理,那么使用 SPSR_EL1, 其他异常等级同理。

4.2 PSTATE 处理器状态

AArch64 并没有与 ARMv7 等同的 CPSR, 而是将 CPSR 的各部分作为可单独访问的不同的位域,这些位域统称为 PSTATE.
PSTATE 的位域定义如下:
ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器_第4张图片
在 AArch64 中,当我们执行 ERET 从异常中返回时,cpu 会将 SPSR_ELn 寄存器的值拷贝到 PSTATE 的各位域中,恢复各算术标志位,执行状态,异常等级,最后处理器跳转到 ELR_ELn 保存的地址处继续执行。

PSTATE.{N, Z, C, V} 位域可以在 EL0 异常等级中访问,其余位域只能 EL1 或更高异常等级中访问,如果在 EL0 中访问,那么是未定义的。

4.3 系统寄存器

在 AArch64 中,系统配置也是通过系统寄存器实现,使用 MSR 与 MRS 指令访问。
而在 ARMv7 中,系统寄存器的访问通过 cp15 实现。在 ARMv8 中,系统寄存器的命名也表明了最低可访问的异常等级,比如:

  1. TTBR0_EL1 表示该寄存器可以在 EL1, EL2, EL3 中访问,而 EL0 不能访问

对于不同异常等级的相同名称的寄存器(即拥有不同的后缀 ELn),它们被认为是单独的寄存器。系统寄存器的访问如下:

MRS x0, TTBR0_EL1 // Move TTBR0_EL1 into x0
MSR TTBR0_EL1, x0 // Move x0 into TTBR0_EL1

AArch64 不支持协处理器的使用,而是直接使用系统寄存器名来访问寄存器。
下表列出了常用的系统控制寄存器:

寄存器名 寄存器描述
ACTRL_ELn 复用控制寄存器,类似于 ARMv7 的 ACTRL, n 取值 1-3
CCSIDR_ELn 当前所选择操作的 cache 的大小寄存器,通常在 clean 和 invalid 操作中使用,n 取值为 1
(后续寄存器描述请参考 4.3 章节内容,有个了解即可,表太多了,懒得翻译了,系统寄存器的名称与功能类似于 ARMv7)

4.3.1 SCTLR_ELn 系统控制寄存器

类似于 ARMv7 的 SCTLR, ARMv8 的 SCTLR_ELn 通用提供 cpu 内核功能控制,比如 cache 使能,MMU 使能。在 AArch64 中,SCTLR 寄存器位定义如下:
ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器_第5张图片
我们可以看出,在 EL2 和 EL3 异常等级中,并不是所有的位都可用,SCTLR 位域描述如下:

位域 位域描述
UCI 当此位设置时,EL0 可以使用一些 cache 管理指令,比如 DC CVAU, DC CIVAC, IC ICAU
EE 当前异常等级端序选择,0 表示选择小端序,1 表示选择大端序
E0E EL0 数据访问端序选择,0表示小端序,1表示大端序
WXN 写权限管理,0 表示内存写权限不被强制为不可执行
nTWE WFE 指令相关
nTWI WFI 指令相关
UCT 此位置位后,EL0 可以访问 CTR_EL0 寄存器
DZE EL0 是否可以访问 DC ZVA 指令,1 表示允许
I EL0/ EL1 instrcution cache 使能位
UMA
SED
ITD
CP15BEN
SA0 EL0 堆栈对齐检查使能位
SA 堆栈对齐检查使能位
C data cache 使能位
A 对齐检查使能位
M MMU 使能位

上述 SCTLR 寄存器位域中,重点关注{I, C, A, M }位域即可。
SCTLR_EL1 访问示例如下:

MRS X0, SCTLR_EL1 // Read System Control Register configuration data
ORR X0, X0, #(1 << 2) // Set [C] bit and enable data caching
ORR X0, X0, #(1 << 12) // Set [I] bit and enable instruction caching
MSR SCTLR_EL1, X0 // Write System Control Register configuration data

4.4 CPU 端序

内存中存在两种基本的字节布局方式:

  1. 小端(LE),word 数据的高字节存储在内存地址的高位
  2. 大端(BE), word 数据的高字节存储在内存地址的地位

如下:
ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器_第6张图片
一个更有效的记忆方式是:大端序对应阅读习惯。而 cpu 一般选择小端序存储数据。
每一个异常等级的端序控制通过当前异常等级的 SCTLR_ELn.SS 位域实现,而 EL0 的端序通过 SCTLR_EL1.E0E 位域控制。

4.5 改变执行状态

上文我们已经知道了只有在异常发生时或从异常返回时才能改变 CPU 执行状态, 现在我们讨论一下执行状态改变时寄存器的变化。
当我们从 AArch32 状态更改为 AArch64 时,我们必须清楚:

  1. 此时寄存器的高32位是未知的,因为此前在 AArch32 状态下我们都是访问的低32位寄存器。
  2. 在 AArch32 执行期间无法访问的寄存器保留它们在 AArch32 执行之前的状态。
  3. 当进入 EL3 异常等级,如果 EL2 使用 AArch32 执行状态,那么 ELR_EL2 的高32位的值未知。
  4. 当执行状态是 AArch32 时,AArch64 的堆栈指针寄存器(SP_EL0/1/2)与异常链接寄存器(ELR_EL1)是不可访问的,他们会保存在 AArch32 执行之前的状态。

通常,应用程序开发人员编写的应用程序为 AArch32 或 AArch64 。只有操作系统必须考虑两种执行状态以及它们之间的切换。

4.5.1 AArch32 状态下的寄存器

AArch32 用于兼容 ARMv7 架构,这意味着 AArch32 必须匹配 ARMv7 的特权分级,也意味着 AArch32 状态下只处理 ARMv7 的 32 位通用目的寄存器。因此,ARMv8 架构与 AArch32 执行状态提供的观点之间必须存在一些对应关系。

ARMv7 存在16个 32 位的通用目的寄存器(R0-R15),R14 是 LR 寄存器,R15 作为 PC寄存器。内核存在 CPSR 寄存器用于保存处理器状态,也存在 SPSR 寄存器用于在异常发生时备份此时的 CPSR。

存在一些寄存器是 banking (不同的模式下,虽然寄存器名一样,但实际的物理缓存地址不同),如下图:
ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器_第7张图片
ARMv7 中 Banking 寄存器的使用减少了异常的延时,但是这也表示在某些模式下,存在一部分寄存器无法使用。
相比较之下,AArch64 拥有 31 个 64 位宽的通用目的寄存器,且这些寄存器在任一异常等级下都可访问。AArch64 与 AArch32 执行状态的改变意味着我们必须把 AArch64 的寄存器映射到 ARMv7 寄存器集合中(包括通用寄存器,特殊寄存器,系统寄存器?)

AArch64 与 AArch32 的通用寄存器映射关系如下:
ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器_第8张图片
在上图中:

  1. AArch32 的 SPSR_svc 映射到 AArch64 的 SPSR_EL1.
  2. AArch32 的 SPSR_hyp 映射到 AArch64 的 SPSR_EL2.
  3. AArch32 的 ELR_hyp 映射到 AArch64 的 ELR_EL2.
  4. SPSR_abt, SPSR_und, SPSR_irq, SPSR_fiq 这 4 个寄存器只能在 AArch32 寄存器中访问, AArch64 不能访问。

4.5.2 AArch32 状态下的 PSTATE

在 AArch64 状态下,ARMv7 的 CPSR 的不同组成部分,作为 PSTATE 的不同位域,可被单独访问。
而在 AArch32 状态下,可直接访问 CPSR 寄存器,此时 CPSR 位域定义如下:
ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器_第9张图片
一些额外的位域只能在 AArch32 下访问:

位域 位域描述
Q
GE(4) 大于或等于标志位
IT(8) if-then 执行位
J
T T32 bit
E 端序选择位
M 执行状态,为1,表示 AArch32

4.6 NEON 与浮点寄存器

除了通用目的寄存器,ARMv8 也拥有 32 个 128 位的浮点寄存器 V0-V31, 这 32 个浮点寄存器用于浮点操作与向量操作

(后续略,暂不了解浮点操作与向量操作)

你可能感兴趣的:(ARM,armv8,arm)