Aarch64是ARM的64位体系结构(有时称为arm64)。 在本文中,我将告诉您它与“常规”(32位)ARM有何不同,以及将系统移植到它上有多困难。
本文不是详细的指南,而是概述了必须重做的那些系统模块,以及整个体系结构与普通的32位ARM有多少不同。 所有这些都是从我将Embox移植到此架构的个人经验得出的 。 对于特定系统的直接移植,您将不得不使用一种或另一种方式来处理文档,在本文结尾处,我留下了一些可能有用的文档的链接。
实际上,差异比相似之处更多,并且Aarch64更像是新架构,而不是熟悉的ARM的64位扩展。 Aarch64的前身主要是Aarch32(这是通常的32位ARM的扩展),但是由于我没有使用它的经验,所以我不会写它:)
在本文的进一步内容中,如果我写的是“旧”或“旧” ARM,我的意思是32位ARM(带有一组ARM命令)。
我将简要介绍与32位ARM相比的更改列表,然后将更详细地分析它们。
- 通用寄存器的宽度增加了2倍(现在每个寄存器为64位),并且其数量增加了一倍(也就是说,现在没有16个,而是32个)。
- 拒绝协处理器寄存器的概念,现在可以简单地通过名称来访问它们,例如
msr vbar_el1, x0
(与之前的mcr p15, 0, %0, c1, c1, 2
msr vbar_el1, x0
mcr p15, 0, %0, c1, c1, 2
msr vbar_el1, x0
相对) - 新的MMU模型(它与旧模型没有任何关系,您必须重新编写)。
- 以前,有两种特权级别:用户(对应于USR处理器模式)和系统(对应于SYS,IRQ,FIQ,ABT等)模式,现在所有东西同时变得更加简单和复杂-现在有4种模式。
- AdvSIMD替换为NEON,浮点操作通过它完成。
现在更多的是重点。
寄存器和指令集
通用寄存器为r0-r30,您可以将其作为64位(x0-x30)或32位(w0-w30,访问低32位)进行访问。
Aarch64的指令集称为A64。 在此处阅读说明的说明。 汇编语言中的基本算术和其他一些命令保持不变:
mov w0, w1 /* w1 w0 */ add x0, x1, 13 /* x0 x1 13 */ b label /* "" "label" bl label /* "" "label", x30 */ ldr x3, [x1, 0] /* x3 , x1 */ str x3, [x0, 0] /* x3 , x0 */
现在介绍一下差异:
- 出现了一个特殊的“零”
rzr/xzr/wzr
,在读取时为零(可以对寄存器使用写操作,但计算结果不会写到任何地方)。
subs xzr, x1, x2 /* x1 x2 NZCV, */
- 您不能一次将许多寄存器(
stmfd sp!, {r0-r3}
)堆叠到堆栈中,而必须成对进行:
stp x0, x1, [sp, 16]! stp x2, x3, [sp, 16]!
PC寄存器(程序计数器,指向当前执行指令的指针)现在不再是通用寄存器(以前是R15),因此无法使用普通命令( mov
, ldr
)只能通过ret
, bl
等进行访问。
程序状态现在不显示CPSR(此寄存器根本不存在),而是显示DAIF寄存器(包含IRQ,FIQ掩码等,AIF-CPSR中的A,I,F位相同),NZCV(负,零,进位) ,oVerflow-突然之间,来自CPSR的相同NZCV)和系统控制寄存器(SCTLR,以启用缓存,MMU,字节序等)。
这些命令似乎足以编写一个简单的引导加载程序,可以将控制权转移到与平台无关的代码:)
性能模式以及它们之间的切换
性能模式在ARMv8-A的基础知识中写得很好,在此我将简要介绍一下本文档的实质。
Aarch64具有4个特权级别(执行级别,以下简称EL)。
- EL3-安全监视器(假定固件正在此级别上运行)
- EL2-虚拟机监控程序
- EL1-操作系统
- EL0-应用
在64位OS上,您可以同时运行32位和64位应用程序。 在32位OS上,只能运行32位应用程序。

EL之间的转换既可以借助异常(系统调用,中断,内存访问错误)来实现,也可以借助异常返回( eret
) eret
。
每个EL都有自己的寄存器SPSR,ELR,SP(即它是“存储寄存器”)。
EL还划分了许多系统寄存器-例如,MMU上下文寄存器ttbr0
是ttbr0_el2
, ttbr0_el1
,您需要访问相应EL上的寄存器。 程序状态寄存器-DAIF,NZCV,SCTLR,SPSR,ELR ...
MMU
Armv8-A支持MMU ARMv8.2 LPA,有关此内容的更多信息,请参见《 ARM体系结构参考手册》(ARMv8)的D5章Armv8-A 。
简而言之,此MMU支持4KiB页(4级虚拟内存表),16KiB(4级)和64KiB(3级)。 在任何中间级别上,您都可以指定一个内存块,从而不指示表的下一级,而是指示下一级表应“覆盖”的整个内存。 我有一篇关于虚拟内存的长期文章 ,您可以在其中阅读有关表,翻译级别等信息。
在较小的更改中,他们拒绝了域,但添加了诸如脏位之类的标志。
通常,除了“块”而不是中间转换表外,没有发现特殊的概念更改,MMU为MMU。
高级SIMD
在使用浮点运算和矢量运算(SIMD)时,旧的NEON中存在明显的AdvSIMD差异。 例如,如果较早的D0由S0和S1组成,而D0和D1由Q0-组成,则现在不是这样:对于Q1-D1和S0,Q0对应于D0和S0,依此类推。 同时,对VFP / SIMD的支持是强制性的,通过调用协议,现在不存在编程参数传递(在GCC中以前称为“ soft float ABI”-标志-mfloat-abi=softfp
),因此您必须实现对浮点的硬件支持。
有16个128位寄存器:

一共有32个128位的寄存器:

您可以在本文中阅读有关NEON的更多信息;可以在此处找到Aarch64可用命令的列表。
浮点寄存器的基本操作:
fadd s0, s1, s2 /* s0 = s1 + s2 */ fmul d0, d1, d2 /* d0 = d1 * d2 */
SIMD基本操作:
/* , : NEON, */ /* q0 = q1 + q2, -- 4 */ vadd.s32 q0, q1, q2 /* : AdvSIMD, */ /* v0 = v1 + v2, -- 4 */ add v0.4s, v1.4s, v2.4s /* v1 ( 2 64- ) d1 */ addv d1, v1.ds /* 4 0 */ movi v1.4s, 0x0
量化宽松
QEMU支持Aarch64。 其中一个平台是virt
,因此它以64位模式启动,您需要另外传递-cpu cortex-a53
,如下所示:
qemu-system-aarch64 -M virt -cpu cortex-a53 -kernel ./embox -m 1024 -nographic # ./embox -- ELF-
很好,此平台使用了许多外围设备,其驱动程序已在Embox中使用-例如,用于控制台的PL011,ARM通用中断控制器等。当然,这些设备具有不同的基寄存器地址和其他中断号,但主要是驱动程序代码在新体系结构上保持不变。 系统启动时,控件位于EL1中。
i.MX8
由于这块铁,开始移植到Aarch64-i.MX8MQ Nitrogen8M。

与QEMU不同,u-boot将控制转移到EL2中的映像,而且由于某种原因,它包括MMU(所有内存都映射为1到1),这在初始化期间会产生一些其他问题。
Embox已经支持i.MX6,并且在i.MX8中,外围部分是相同的-例如,UART和以太网也可以工作(我必须修复几个紧密绑定到32位地址的地方)。 另一方面,中断控制器在那里有所不同-ARM GICv3,它与第一个版本完全不同。
结论
目前,Embox对Aarch64的支持还不完整,但是功能已经很少了-中断,MMU,通过UART的输入输出。 还有很多工作有待完成,但第一步比一开始看起来就容易做。 与ARM相比,文档和文章要少得多,但是有足够的信息来处理所有问题。
通常,如果您具有ARM的经验,则移植到Aarch64是可行的任务。 尽管像往常一样,您可能会偶然发现一些小问题:)
如果您有任何问题,可以从我们的资源库中下载该项目以在QEMU中戳它-在评论中,在新闻通讯中或在Telegram的聊天室中聊天 (也有一个频道 )。
有用的链接
聚苯乙烯
8月24日至25日,我们将在TechTrain上演讲,听我们的表演大约两到 三遍 ,来到展台-我们将回答您的问题:)