···························································· 第3 章 chapter3 ARMv8架构基础知识 ARMv8架构是ARM 体系结构的一种,它具有引人注目的特点:首次引入了64位 指令集,从而能够处理更大的内存空间和更复杂的数据计算。此外,ARMv8架构还引入 了全新的执行模式AArch64,它能够运行64位指令集,支持更广泛的操作模式和高级特 性。目前,基于ARMv8架构设计的处理器在各个领域都发挥着重要的作用。 本章将从ARMv8架构的基础概念、寄存器组、A64指令集、ARM64异常处理以及 ARM64内存管理等方面介绍ARMv8架构,旨在帮助读者更好地理解ARMv8架构。 3.1 ARMv8 架构 ARMv8是ARM 公司推出的处理器架构,广泛应用于移动设备、嵌入式系统、物联 网设备和云服务器等领域,了解ARMv8架构可以理解和应用这些设备和系统。许多应 用和系统都是基于ARMv8架构开发的,学习ARMv8架构可以更好地理解和开发这些 应用和系统。同时,了解ARMv8架构也可以提高编程技能和工作竞争力。本节主要对 ARMv8中涉及的主要概念进行梳理,介绍基于ARMv8架构设计的处理器的运行状态 和其支持的数据宽度。 3.1.1 ARMv8 架构介绍 1.ARMv8-A 架构特性 ARMv8-A 是ARM 公司发布的第一代支持64位处理器的指令集和架构,它在扩充 64位寄存器的同时提供了对上一代架构指令集的兼容,因此它提供了运行32位和64位 应用程序的环境。 ARMv8-A 架构除了提高了处理能力,还引入了很多吸引人的新特性。 . 具有超大物理地址空间,提供超过4GB物理内存。 . 具有64位宽的虚拟地址空间。32位宽的虚拟地址空间只能提供4GB大小的虚 拟地址空间访问,这极大地限制了桌面操作系统和服务器等的性能发挥。64位 宽的虚拟地址空间可以提供更大的访问空间。 . 提供31个64位宽的通用寄存器,可以减少对栈的访问,从而提高性能。 5 0 ◆嵌入式系统开发与应用 . 提供16KB和64KB的页面,有助于降低TLB的未命中率(missrate)。 . 具有全新的异常处理模型,有助于降低操作系统和虚拟化的实现复杂度。 . 具有全新的加载-获取指令(load-acquireinstruction)、存储-释放指令(storereleaseinstruction), 专门为C++11、C11以及Java内存模型设计。 2.采用ARMv8架构的常见处理器内核 下面介绍市面上常见的采用ARMv8架构的处理器(简称ARMv8处理器)内核。 (1)Cortex-A53处理器内核。ARM 公司第一款采用ARMv8-A 架构的处理器内 核,专门为低功耗设计的处理器。通常可以使用1~4个Cortex-A53处理器组成一个处 理器簇,或者和Cortex-A57或Cortex-A72等高性能处理器组成大/小核架构。 (2)Cortex-A57处理器内核。采用64位ARMv8-A 架构的处理器内核,而且通过 AArch32执行状态,保持与ARMv7架构完全后向兼容。除了ARMv8架构的优势之外, Cortex-A57还提高了单个时钟周期的性能,比高性能的Cortex-A15高出20%~40%; 它还改进了二级高速缓存的设计和内存系统的其他组件,极大地提高了性能。 (3)Cortex-A72处理器内核。2015年年初正式发布的基于ARMv8-A 架构,并在 Cortex-A57处理器上做了大量优化和改进。在相同的移动设备电池寿命限制下, Cortex-A72相较于基于Cortex-A15的设备具有3.5倍的性能提升,展现出了优异的整 体功耗效率。 3.1.2 ARMv8 基础概念 ARM 处理器实现的是精简指令集架构。在ARMv8-A架构中有以下基本概念和定义。 (1)处理机(processingelement,PE)。在ARM 公司的官方技术手册中提到的一个 概念,把处理器处理事务的过程抽象为处理机。 (2)执行状态(executionstate)。处理器运行时的环境,包括寄存器的位宽、支持的 指令集、异常模型、内存管理以及编程模型等。ARMv8架构定义了两个执行状态。 ① AArch64:64位的执行状态。 . 提供31个64位的通用寄存器。 . 提供64 位的程序计数器(program counter,PC)指针寄存器、栈指针(stack pointer,SP)寄存器以及异常链接寄存器(exceptionlinkregister,ELR)。 . 提供A64指令集。 . 定义ARMv8异常模型,支持4个异常等级,即EL0~EL3。 . 提供64位的内存模型。 . 定义一组处理器状态(PSTATE)用来保存PE的状态。 ② AArch32:32位的执行状态。 . 提供13个32位的通用寄存器,再加上PC指针寄存器、SP寄存器、链接寄存器 (linkregister,LR)。 . 支持两套指令集,分别是A32和T32指令集(Thumb指令集)。 . 支持ARMv7-A 异常模型,基于PE模式并映射到ARMv8的异常模型中。 第◆3 章 ARMv8 架构基础知识5 1 . 提供32位的虚拟内存访问机制。 . 定义一组PSTATE用来保存PE的状态。 (3)ARMv8指令集。ARMv8架构根据不同的执行状态提供不同指令集的支持。 . A64指令集:运行在AArch64状态下,提供64位指令集支持。 . A32指令集:运行在AArch32状态下,提供32位指令集支持。 . T32指令集:运行在AArch32状态下,提供16和32位指令集支持。 (4)系统寄存器命名。在AArch64状态下,很多系统寄存器会根据不同的异常等级 提供不同的变种寄存器。 3.1.3 ARMv8 处理器的运行状态 ARMv8处理器支持两种执行状态———AArch64状态和AArch32状态。AArch64 状态是ARMv8新增的64位执行状态,而AArch32是为了兼容ARMv7架构的32位执 行状态。当处理器运行在AArch64状态下时,运行A64指令集;而当运行在AArch32 状态下时,可以运行A32指令集或者T32指令集。 如图3-1所示,AArch64状态的异常等级(exceptionlevel)决定了处理器当前运行的 特权级别,类似于ARMv7架构中的特权等级。 图3-1 AArch64状态的异常等级 . EL0:用户特权,用于运行普通用户程序。 . EL1:系统特权,通常用于运行操作系统。 . EL2:运行虚拟化扩展的虚拟监控程序(hypervisor)。 . EL3:运行安全世界中的安全监控器(securemonitor)。 ARMv8架构允许切换应用程序的运行模式。例如在一个运行64位操作系统的 ARMv8处理器中,可以同时运行A64指令集的应用程序和A32指令集的应用程序。但 是在一个运行32位操作系统的ARMv8处理器中就不能运行A64指令集的应用程序 了。当需要运行A32指令集的应用程序时,需要通过一条管理员调用(supervisorcall, SVC)指令切换到EL1,操作系统会做任务的切换并返回AArch32的EL0中,这时操作 系统就为这个应用程序准备好了AArch32的运行环境。 3.1.4 ARMv8 架构支持的数据宽度 ARMv8支持如下几种数据宽度。 5 2 ◆嵌入式系统开发与应用 . 字节(byte):8位。 . 半字(halfword):16位。 . 字(word):32位。 . 双字(doubleword):64位。 . 四字(quadword):128位。 不对齐访问有两种情况,一种是指令不对齐访问,另一种是数据不对齐访问。A64 指令集要求指令存放的位置必须以字(word,32位宽)为单位对齐。访问一条存储位置 不以字为单位对齐的指令会导致PC对齐异常(PCalignmentfault)。 对于数据访问,需要区分不同的内存类型。内存类型是设备内存的不对齐访问会触 发一个对齐异常(alignmentfault)。 对于访问普通内存,除了独占加载/独占存储(load-exclusive/store-exclusive)指令或 者加载-获取/存储-释放(load-acquire/store-release)指令外,对于其他加载或者存储单个 或多个寄存器的所有指令,如果访问地址和要访问数据不对齐,则按照以下两种情况进 行处理。 . 若对应的异常等级中的SCTLR_Elx.A 设置为1,则说明打开了地址对齐检查功 能,那么会触发一个对齐异常。 . 若对应的异常等级中的SCTLR_Elx.A 设置为0,那么处理器支持不对齐访问。 当然,处理器对不对齐访问也有一些限制。 . 不能保证单次原子地完成访问,可能多次复制。 . 不对齐访问比对齐访问需要更多的处理时间。 . 不对齐访问可能会造成中止(abort)。 3.2 ARMv8 寄存器 了解ARMv8寄存器的结构、数量和使用方式,对理解指令集的执行、优化代码、调试 程序以及进行性能分析非常有必要。因此本节通过介绍ARMv8架构的主要寄存器:通 用寄存器、处理状态寄存器、特殊寄存器和系统寄存器,帮助读者认识ARMv8寄存器。 3.2.1 通用寄存器 AArch64运行状态支持31 个64 位的通用寄存器,分别是X0~X30 寄存器,而 AArch32状态支持16个32位的通用寄存器。 通用寄存器除了用于数据运算和存储之外,还可以在函数调用过程中起到特殊作 用,ARM64架构的函数调用标准和规范对此有所约定,如图3-2所示。 在AArch64状态下,使用X表示64位通用寄存器,如X0、X30等。另外,还可以使 用W 表示X寄存器的低32位的数据,如W0表示X0寄存器的低32位数据,W1表示 X1寄存器的低32位数据,如图3-3所示。 第◆3 章 ARMv8 架构基础知识5 3 图3-2 AArch64状态的31个通用寄存器 图3-3 64位通用寄存器和低32位数据 3.2.2 处理器状态寄存器 在ARMv7架构中,使用当前程序状态寄存器(currentprogramstatusregister, CPSR)表示当前的处理器状态(processorstate),而在AArch64中使用PSTATE寄存器 表示,如表3-1所示。 表3-1 PSTATE寄存器 分 类字 段描 述 条件标志位 N 负数标志位 在结果是有符号的二进制代码的情况下,如果结果为负数,则N=1;如果 结果为非负数,则N=0 Z 0标志位 如果结果为0,则Z=1;如果结果为非0,则Z=0 C 进位标志位 当发生无符号数溢出时,C=1 其他情况下,C=0 V 有符号数溢出标志位 对于加/减指令,在操作数和结果是有符号的整数时,如果发生溢出,则V =1;如果未发生溢出,则V=0 对于其他指令,V通常不发生变化 续表 5 4 ◆嵌入式系统开发与应用 分 类字 段描 述 运行状态控制 SS 软件单步。该位为1,说明在异常处理中使能了软件单步功能 IL 不合法的异常状态 nRW 当前执行模式 0:处于AArch64状态 1:处于AArch32状态 EL 当前异常等级 0:表示EL0 1:表示EL1 2:表示EL2 3:表示EL3 SP 选择SP寄存器。当运行在EL0时,处理器选择EL0的SP寄存器,即SP_ EL0;当处理器运行在其他异常等级时,处理器可以选择使用SP_EL0或者 对应的SP_ELn寄存器 异常掩码标志位 D 调试位。使能该位可以在异常处理过程中打开调试断点和软件单步等 功能 A 用来屏蔽系统错误(SError) I 用来屏蔽IRQ F 用来屏蔽FIQ 访问权限 PAN 特权不访问(privilegedaccessnever)位是ARMv8.1的扩展特性 1:在EL1或者EL2访问属于EL0的虚拟地址时会触发一个访问权限 错误 0:不支持该功能,需要软件模拟 UAO 用户特权访问覆盖标志位,是ARMv8.2的扩展特性 1:当运行在EL1或者EL2时,没有特权的加载存储指令可以和有特权 的加载存储指令一样访问内存,如LDTR指令 0:不支持该功能 3.2.3 特殊寄存器 ARMv8架构除了支持31个通用寄存器之外,还提供多个特殊寄存器,如图3-4 所示。 1.零寄存器 ARMv8架构提供两个零寄存器(zeroregister),这些寄存器的内容全是0,可以用作 源寄存器,也可以用作目标寄存器。WZR寄存器是32位的零寄存器,XZR是64位的零 寄存器。 ◆ 第 3 章 ARMv8 架构基础知识55 图3- 4 特殊寄存器 2.PC寄存器 PC寄存器通常用来指向当前运行指令的下一条指令的地址,用于控制程序中指令 的运行顺序,但是编程人员不能通过指令直接访问它。 3.SP寄存器 ARMv8架构支持4个异常等级,每一个异常等级都有一个专门的SP寄存器SP_ ELn,例如处理器运行在EL1时选择SP_EL1寄存器作为SP寄存器。 .SPEL0:EL0下的SP寄存器。 .SPL1:EL1下的SP寄存器。E(_) .SPL2:EL2下的SP寄存器。E(_) .SP_L3:EL3下的SP寄存器 。 当处理器运行在比EL0高的异常等级时,处理器可以访问如下寄存器 。E(_) .当前异常等级对应的SP寄存器SP_ELn。 .EL0对应的SP寄存器SP_EL0可以当作一个临时寄存器,如Linux内核里使用 该寄存器存放进程的task_struct数据结构的指针。 当处理器运行在EL0时,它只能访问SP_EL0,而不能访问其他高级的SP寄存器。 4.保存处理状态寄存器 当运行一个异常处理器时,处理器的处理状态会保存到保存处理状态寄存器(saved processtatusregister,SPSR)中,这个寄存器非常类似于ARMv7架构中的CPSR 。当 异常将要发生时,处理器会把PSTATE寄存器的值暂时保存到SPSR中;当异常处理完 成并返回时,再把SPSR的值恢复到PSTATE寄存器。SPSR的格式如图3-5所示, SPSR的重要字段如表3-2所示。 ◆ 56 嵌入式系统开发与应用 图3- 5 SPSR 的格式 表3- 2 SPSR 的重要字段 字段描述 N 负数标志位 Z 零标志位 C 进位标志位 V 有符号数溢出标志位 DIT 与数据无关的指令时序(dataindependenttiming),ARMv8.4的扩展特性 UAO 用户特权访问覆盖标志位,ARMv8.2的扩展特性 PAN 特权模式禁止访问(privilegedacesnever)位,ARMv8.1的扩展特性 SS 表示是否使能软件单步功能。若该位为1,说明在异常处理中使能了软件单步功能 IL 不合法的异常状态 D 调试位。使能该位可以在异常处理过程中打开调试断点和软件单步等功能 A 用来屏蔽系统错误 I 用来屏蔽IRQ F 用来屏蔽FIQ M[4] 用来表示异常处理过程中处于哪个执行状态,若为0,表示AArch64状态 M[3:0] 异常模式 5.ELR 寄存器 该寄存器存放了异常返回地址。 6.CurentEL寄存器 该寄存器表示PSTATE寄存器中的EL字段,其中保存了当前异常等级。使用 MRS指令可以读取当前异常等级。 .0:表示EL0 。 .1:表示EL1 。 .2:表示EL2 。 第◆3 章 ARMv8 架构基础知识5 7 .3:表示EL3。 7.DAIF寄存器 该寄存器表示PSTATE寄存器中的{D,A,I,F}字段。 8.SPSel寄存器 该寄存器表示PSTATE寄存器中的SP字段,用于在SP_EL0和SP_ELn中选择SP 寄存器。 9.PAN 寄存器 该寄存器表示PSTATE寄存器中的PAN(privilegedaccessnever,特权禁止访问) 字段。可以通过MRS和MSR指令设置PAN 寄存器。 10.UAO 寄存器 该寄存器表示PSTATE寄存器中的UAO(useraccessoverride,用户访问覆盖)字 段。可以通过MRS和MSR指令设置UAO 寄存器。 11.NZCV 寄存器 该寄存器表示PSTATE寄存器中的{N,Z,C,V }字段。 3.2.4 系统寄存器 除了上面介绍的通用寄存器和特殊寄存器之外,ARMv8架构还定义了很多的系统 寄存器,通过访问和设置这些系统寄存器可以完成对处理器不同功能的配置。在 ARMv7架构中,需要通过访问CP15协处理器间接访问这些系统寄存器,而在ARMv8 架构中没有协处理器,可直接访问系统寄存器。ARMv8架构支持如下7类系统寄存器。 . 通用系统控制寄存器。 . 调试寄存器。 . 性能监控寄存器。 . 活动监控寄存器。 . 统计扩展寄存器。 . RAS寄存器。 . 通用定时器寄存器。 系统寄存器支持不同异常等级的访问,通常系统寄存器会使用Reg_ELn的方式 表示。. Reg_ELl:处理器处于EL1、EL2以及EL3时可以访问该寄存器。 . Reg_EL2:处理器处于EL2和EL3时可以访问该寄存器。 . 大部分系统寄存器不支持处理器处于EL0时访问,但也有一些例外,如CTR_ EL0寄存器。 5 8 ◆嵌入式系统开发与应用 程序可以通过MRS和MSR指令访问系统寄存器。 MRS X0, TTBR0_EL1 //把TTBR0_EL1 的值复制到x0 寄存器 MSR TTBR0_EL1, X0 //X0 寄存器的值复制到TTBR0_EL1 3.3 A64 指令集 指令集是处理器体系结构设计的重点之一。ARM 公司定义和实现的指令集一直在 变化和发展中。ARMv8体系结构最大的改变是增加了一个新的64位指令集,这是早前 ARM 指令集的有益补充和增加,它可以处理64位宽的寄存器和数据,并且使用64位的 指针访问内存。这个新的指令集称为A64指令集,运行在AArch64状态。ARMv8兼容 旧的32位指令集———A32指令集,它运行在AArch32状态。A64和A32指令集并不兼 容,它们是两套不同的指令集,指令编码是不一样的。需要注意的是,A64指令集支持64 位宽的数据和地址寻址,但其编码宽度是32位,而不是64位。A64指令集具有以下特 点:具有特有的指令编码格式;只能运行在AArch64状态;指令的宽度为32位。 下面以前变基模式的LDR指令为例,介绍A64指令集的编码风格,如图3-6所示。 图3-6 前变基模式的LDR 指令的编码 第0~4位为Rt字段,用来描述目标寄存器Xt,可以从X0~X30中选择。 第5~9位为Rn字段,用来描述基地址寄存器Xn,可以从X0~X30中选择,也可以 选择SP寄存器作为第31个寄存器。 第12~20位为imm9字段,用于偏移量imm。 第21~29位用于指令分类。 第30~31位为size字段,当size为0b11时,表示64位宽数据;当size为0b10时,表 示32位宽数据。 A64指令集可以分为如下几类: . 内存加载和存储指令; . 多字节内存加载和存储指令; . 算术和移位指令; . 移位操作指令; . 位操作指令; . 条件操作指令; . 跳转指令; . 独占访问指令; 第◆3 章 ARMv8 架构基础知识5 9 . 内存屏障指令; . 异常处理指令; . 系统寄存器访问指令。 3.3.1 加载与存储指令 和早期的ARM 体系结构一样,ARMv8体系结构基于指令加载和存储体系结构。 在这种体系结构下,所有的数据处理都需要在通用寄存器中完成,而不能直接在内存中 完成。因此,首先把待处理数据从内存加载到通用寄存器,然后进行数据处理,最后把数 据写入内存。 常见的内存加载指令是LDR 指令,内存写入指令是STR 指令。LDR 指令和STR 指令的基本格式如下。 LDR 目标寄存器, <存储器地址> //把存储器地址中的数据加载到目标寄存器中 STR 源寄存器, <存储器地址> //把源寄存器的数据存储到存储器中 1.基地址模式的寻址 基地址模式首先是使用寄存器的值表示一个地址,然后把这个内存地址的内容加载 到通用寄存器中。 以下指令以Xn寄存器中的内容作为内存地址,加载此内存地址的内容到Xt寄 存器。 LDR Xt, [Xn] 以下指令是把Xt寄存器中的内容存储到Xn寄存器的内存地址中。 STR Xt, [Xn] 2.基地址加偏移量模式的寻址 基地址加偏移量模式是指在基地址的基础上再加上偏移量,从而计算内存地址,并 把这个内存地址的值加载到通用寄存器中,偏移量可以是正数,也可以是负数。 以下指令是把Xn寄存器的内容加上一个偏移量(offset必须是8的倍数),以相加的 结果作为基地址,加载此内存地址的内容到Xt寄存器。 LDR Xt, [Xn, #offset] 基地址加偏移量模式的存储指令格式如下。该指令是将Xt寄存器中的值存储到以 Xn寄存器的值加一个偏移量(offset必须是8的倍数)表示的地址中。 STR Xt, [Xn, #offset] 3.基地址扩展模式的寻址 基地址扩展模式的命令如下。 6 0 ◆嵌入式系统开发与应用 LDR , [, (){,{}}] STR , [, (){,{}}] Xt:目标寄存器。 Xn:基地址寄存器。 Xm:用来表示偏移的寄存器。 extend:扩展/移位指示符,默认是LSL,也可以是UXTW、SXTW、SXTX。 amount:索引偏移量,amount的值只能是0或者3,如果是其他值,汇编器将报错。 【例3-1】 如下代码使用了基于基地址加偏移量模式。 LDR X0, [X1] //内存地址为X1 寄存器的值,加载此内存地址的值到X0 寄存器 LDR X0, [X1,#8] //内存地址为X1 寄存器的值再加上偏移量(8),加载此内存地址的值到X0 寄存器 LDR X0,[X1,X2] //内存地址为X1 寄存器的值加X2 寄存器的值,加载此内存地址的值到X0 寄存器 LDR X0,[X1,X2,LSL #3] //内存地址为X1 寄存器的值加(X2 寄存器的值< < 3),加载此内存地址的值到X0 寄存器 LDR X0,[X1,W2,SXTW] //先对W2 的值做有符号的扩展,和X1 寄存器的值相加后,将结果作为内存地址, //加载此内存地址的值到X0 寄存器 LDR X0,[X1,W2,SXTW #3] //先对W2 的值做有符号的扩展,然后左移3 位,和X1 寄存器的值相加后,将 //结果作为内存地址,加载此内存地址的值到X0 寄存器 4.前变基模式的寻址 前变基模式指先更新偏移量地址,后访问内存地址。前变基模式的指令格式如下。 首先更新Xn|SP寄存器的值为Xn|SP寄存器的值加imm,然后以新的Xn|SP的值为内 存地址,加载该内存地址的值到Xt寄存器。 LDR , [, #]! 以下指令首先更新Xn|SP的值为Xn|SP寄存器的值加imm,然后把Xt寄存器的值 存储到Xn|SP寄存器的新值为地址的内存单元中。 STR , [, #]! 5.后变基模式的寻址 后变基模型指先访问内存地址,后更新偏移量地址。首先以Xn|SP寄存器的值为内 存地址,取该内存地址上的值到Xt寄存器,再更新Xn|SP寄存器的值为Xn|SP寄存器 的值加imm。 第◆3 章 ARMv8 架构基础知识6 1 LDR , [], # 以下指令先将Xt寄存器的值存储到以Xn|SP寄存器的值为地址的内存单元中,然 后更新Xn|SP寄存器的值为Xn|SP寄存器的值加imm。 STR , [], # 【例3-2】 如下代码使用了前变基模式和后变基模式。 LDR X0, [X1, #8]! //前变基模式。先更新X1 寄存器的值为X1 寄存器的值加8 //然后以新的X1 寄存器的值为内存地址,加载该内存地址的值到X0 寄存器中 LDR X0, [X1], #8 //后变基模式。以X1 寄存器的值为内存地址,加载该内存地址的值到X0 寄存器,然后更 //新X1 寄存器的值为X1 寄存器的值加8 6.PC相对地址模式的寻址 汇编代码中常常会使用标签(label)标记代码片段。LDR 指令还提供一种访问标签 的地址模式,指令的格式如下。这条指令驱动label所在内存地址的内容到Xt寄存器 中。但是这个label必须在当前PC地址前后1MB的范围内,如果超过这个范围,则汇编 器会报错。 LDR ,