本文主要是介绍Supporting 64-bit ARM systems,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 前言
- 一、ARMv8简介
- 二、Linux对ARMv8的支持
- 三、补丁
- 3.1 initial GCC patches
- 3.2 AArch64 Linux kernel port
- 四、64位ARM独立实现
- 参考资料
前言
By Jonathan Corbet
July 10, 2012
最新的ARM CPU型号ARM v8首次为ARM世界添加了64位内存寻址功能。新的64位CPU可以运行32位代码,但64位指令集是全新的,而不仅仅是对32位指令集的64位扩展,因此Linux支持已被实现为一种全新的体系结构。
2012年ARM公司发布了64位 Cortex-A53 和 Cortex-A57处理器内核。
Linux 于 3.7 内核版本主线实现 ARM 64-bit support。
一、ARMv8简介
ARMv8是ARM公司发布的第一代支持64位处理器指令集和体系架构,ARMv8架构引入了64位指令集(AArch64),与之前的32位指令集(A32/T32)相比,具有更大的寻址空间和更高的性能潜力。它支持更大的物理和虚拟地址空间,能够处理更大的数据集和执行更复杂的计算任务。
提供31个64位宽的通用寄存器,可以减少对栈的访问,从而提供性能。
ARMv8架构引入了更复杂的特权级别体系,以支持虚拟化和安全性增强。它提供了更多的特权级别和更细粒度的控制,使操作系统和虚拟化软件能够更好地管理系统资源和隔离不同的执行环境。
EL0 Applications.
EL1 OS kernel and associated functions that are typically described as privileged.
EL2 Hypervisor.
EL3 Secure monitor.
ARMv8体系架构定义了两个执行状态:AArch64 和 AArch32。
AArch64 是 ARMv8新增的执行状态,而AArch32是为了兼容ARMv7体系架构的32位执行状态。
当处理器运行在AArch64状态下时,运行A64指令集;当处理器运行在AArch32状态下时,运行A32/T32指令集。
ARMv8架构的处理器可以在AArch64和AArch32(A32/T32)之间切换执行状态,以兼容不同的应用需求。在执行状态切换时,处理器会切换指令集、寄存器状态和特权级别,以适应不同的代码和运行环境。
ARMv8指令集:
ARMv8指令集包括多个指令集,其中包括A64指令集、A32指令集和T32指令集。这些指令集适用于不同的执行状态和编码格式。
(1)A64指令集(64位指令集):AArch64 – 提供了64位指令集支持。
(2)A32指令集(32位指令集):AArch32 – 提供了32位指令集支持。
(3)T32指令集(32位指令集 – Thumb指令集):AArch32 – 提供了16位/32位指令集支持。
AMRv8兼容旧的32位指令集,运行在AArch32状态下。
备注:A64指令集和A32指令集是不兼容的,是两套完全不一样的指令集,指令编码是不一样的。但是A64指令集的指令宽度是32位,不是64位。
AArch64状态下只能运行A64指令集,如果要运行32指令集,那么ARMv8架构就要将执行状态从AArch64切换位AArch32。
一个App 可以混合使用 T32 和 A32, 但是不能混合使用 A32 和 A64。
执行A64指令的处理器处于AArch64状态。在此状态下,指令可以访问64位和32位寄存器。
执行A32或T32指令的处理器处于AArch32状态。在此状态下,指令只能访问32位寄存器,无法访问64位寄存器。
基于ARMv8架构的处理器可以运行针对AArch32和AArch64状态构建的应用程序,但在AArch32和AArch64状态之间的切换只能在异常边界(例如处理中断或系统调用时)发生。
ARM编译器工具链可为AArch32状态或AArch64状态构建映像。因此,使用ARM编译器工具链构建的映像可以只包含A32和T32指令或只包含A64指令。
处理器只能执行与其当前执行状态匹配的指令集。处于AArch32状态的处理器无法执行A64指令,而处于AArch64状态的处理器无法执行A32或T32指令。必须确保处理器始终接收与当前执行状态相匹配的指令集。
二、Linux对ARMv8的支持
ARM是有史以来最成功的处理器架构之一;我们大多数人拥有的ARM核心数量要超过x86处理器的数量。ARM被广泛视为嵌入式系统处理器,它专注于最小功耗和能够构建成各种系统芯片配置。ARM作为“小型系统”的形象在很大程度上受到鼓励,因为ARM处理器仅支持32位。然而,随着64位ARM处理器的出现,这种情况即将发生改变。Linux将为这些系统做好准备 - 第一批64位ARM支持补丁刚刚发布 - 但还存在一些关于几个基本决策的争议。
确实有人会质疑是否真的需要64位ARM处理器。对于最高级的手机或平板电脑来说,64位计算似乎有些过于高端,更不用说ARM处理器主导的嵌入式控制器了。但是移动设备开始推动32位系统的内存寻址限制;即使是1GB的系统,在大多数配置中都需要使用高内存。因此,即使强烈预期的ARM服务器系统从未实现,为了能够高效地使用未来移动设备的内存,仍将需要64位ARM处理器。“移动"和"嵌入式"不再意味着"微小”。
随着移动设备的发展,其功能和需求不断增加。现代移动设备具有更大的内存需求,以支持复杂的应用程序、多任务处理和更高的性能要求。因此,64位处理器能够提供更大的内存寻址空间和更高的计算能力,有助于满足这些需求。
确实,对于成功引入64位ARM处理器来说,Linux的支持是一个重要的前提条件,因此ARM已经在这个领域支持相关工作已有一段时间。最初的GCC补丁是在今年5月发布的,而第一批内核补丁是由Catalin Marinas于7月6日发布的。尽管目前还没有64位ARM硬件可用,但所有这些代码都是在模拟器上开发的。一旦硬件问世,幸运的是,软件将能够在最小程度的调整下正常工作。
64位ARM支持需要通过一个由36个部分组成的补丁集添加数千行新代码。其中包含了一些新颖的功能,比如能够以64KB的本机内存页大小运行,并且需要对许多重要的技术决策进行审查。因此,内核开发者们按照人们的预期开始对架构名称提出了一些抱怨。这个名称(“AArch64”)对很多人来说既显得多余(当然它是一种架构),又缺乏信息("A"代表什么?)。很多人更希望使用ARMv8(这是实际硬件架构的名称,"AArch64"是ARMv8的64位操作模式)或者arm64这样的名称。
对于名称的选择往往是一个主观的问题,在不同的社区和项目中可能有所不同。就像其他讨论一样,关于命名惯例的争论是正常的。最终,架构名称的最终决定将取决于内核开发社区中达成的共识。然而,围绕名称的讨论不应掩盖在Linux内核中为支持64位ARM处理器所做的技术工作的重要性。
支持当前名称的理由包括该名称已经在用于二进制文件的ELF三元组中标识该架构;在各个地方使用相同的名称应有助于减少混淆。但正如Arnd Bergmann所指出的那样:“如果其他所有东西都是aarch64,我们应该在内核目录中也使用这个名字,但如果每个人都称其为arm64,我们可能应该尽可能多地使用那个名字”。Jon Masters以经典的反对风格表示喜欢当前的名称;Fedora计划在其64位ARM版本中使用"aarch64"作为名称。其他人,如Ingo Molnar,主张在现在相对容易的时候更改名称。Catalin似乎倾向于保留当前的名称,但表示在发布下一个版本的补丁系列之前会考虑一下。
一些开发者提出了一个更具实质性的问题:从一开始就将32位和64位ARM实现统一起来是否更有意义?其他一些架构(如x86、PowerPC、SPARC和MIPS)都是先分别实现,然后在后期将它们合并,通常会伴随着一些重大困难。为了避免将来的ARM开发者面临这些困难,有人建议或许最好从一个统一的实现开始。
比如对于x86架构:
目前的内核版本x86(32位)和 x86_64(64位)都是在/arch/x86 目录下。
对于arm架构:
而arm32在/arch/arm目录下。
而arm64在/arch/arm64目录下。
有许多原因解释了为什么要进行独立的64位ARM架构实现。Arnd的这份说明中包含了相关思考的大部分内容。64位ARM指令集与32位版本完全不同,以至于无法编写适用于两种架构的汇编代码。系统调用接口也存在显著差异,64位版本采用更标准的方法,并且摒弃了许多遗留代码。64位实现还希望将整个32位ARM "平台"概念抛在身后;事实上,正如Jon所说,人们希望从一开始就能够在所有64位ARM系统上运行单个内核二进制文件。总体上说,给AArch64一个独立的顶层层次结构将有助于摆脱大量的ARM历史包袱,并且将带来更好的整体实现。
独立的64位ARM实现有多个优势和目标。首先,64位ARM指令集与32位版本完全不同,因此需要一个独立的实现来满足其特定的需求和特征。其次,系统调用接口在64位版本中采用了更标准化的方法,这意味着可以摒弃许多旧有的、与32位版本相关的代码,从而简化和优化实现。此外,从一开始就将AArch64置于独立的顶层层次结构中,可以摆脱许多ARM的历史包袱,使得实现更加清晰、简洁,并有望带来更好的性能和可维护性。
一些人迅速指出,大多数这些论点在其他架构的背景下也听过。x86_64架构最初也被视为一个全新的起点,摒弃了许多旧有的i386代码。但最终情况并非如此。这里可能存在不同的情况;相比其他架构,32位ARM具有更多的旧有包袱,而且处理器之间的差异似乎更大。有人说,适当的比较是与x86和ia64,尽管人们可以感觉到AArch64开发者总体上不希望被视为与ia64相同。
最终,这个决策将取决于AArch64开发者的意愿;由他们来提供一个可行的实现,并在未来进行维护。如果他们坚持认为应该将其作为独立的顶层架构,单凭这个原因很少有人会阻止其合并。当然,如果将来合并两者成为必要,也将取决于这些开发者来进行管理。作为独立的顶层架构,它还可以让开发者进行一些实验,而不必担心破坏旧有的32位系统;结果可能是在未来几年内出现一个更好的统一架构,如果情况朝着这个方向发展的话。
到目前为止,对AArch64补丁集的技术批评还很少。事情可能会继续保持这样的状态。这些代码已经经过多轮私下评审,参与其中的知名开发者已经发现并解决了最严重的问题。很少有开发者对这种新处理器有足够的理解,以真正理解代码的大部分内容。因此,它可能会在主线内核中被合并(可能在3.7版本之后),而不需要太多实质性的更改。之后,唯一需要的就是实际的硬件,然后事情将变得真正有趣起来。
三、补丁
3.1 initial GCC patches
ARM很高兴宣布将GCC移植到AArch64架构上。
需要注意的是,虽然这个编译器已被用于构建大量软件,但它还不能被认为是完整的。我们预计在代码集成到主编译器之前,还会有一些尚未发现的错误和其他问题需要解决。尽管如此,我们认为现在的代码已经达到了一个值得进行公开评审的状态,以便我们能够及时解决未解决的问题,以适应GCC-4.8版本发布的时间。
在我们做出的一些更有争议的设计决策中,我们选择了将该编译器与现有的ARM编译器分开。我们在内部进行了一番辩论,最后得出结论,保持源代码分开是正确的做法,理由如下:
(1)AArch64和AArch32状态之间不存在函数调用级别的兼容性;只能在异常级别边界(例如用户态和内核态之间)进行交互处理。
(2)A64指令集在几个重要方面与ARM不同
寄存器数量更多
没有广义条件执行
寄存器命名方式有很大差异
A64与A32和T32指令助记符之间最多只存在相似之处。特别是,虽然Neon提供类似的功能,但指令却非常不同。
(3)AArch64中的浮点寄存器架构与AArch32不同。
(4)大多数现有的ARM特定命令行选项不适用于AArch64;混合使用两者会给用户带来困惑。
(5)移植后端中很少有代码可以直接共享;除了维护开销之外,共享代码还会在运行时产生性能开销。
(6)通过从零开始,我们能够充分利用GCC现在包含的一些新构造。
也许唯一存在潜在共享机会的领域是流水线描述。然而,我们相信在这种情况下,在两个后端中使用相同的描述是可行的。
仍然有一些组件需要进一步完善。我们已经了解到的问题(在较粗略的层面上)包括:
改进arm_neon.h的实现和自动向量化
支持除C、C++和Fortran之外的其他语言
原子操作优化表支持
进一步完善内存和TLS模型
一些扩展编译模式,例如性能分析支持、libmudflap、堆栈保护器。
如果能够与社区专家公开讨论这些问题,其中一些问题将更容易解决。
3.2 AArch64 Linux kernel port
这套补丁集旨在为AArch64(64位ARM)架构提供Linux的核心支持。
AArch64是作为ARMv8架构的一部分而引入的,它包括一个经过大幅改进的异常模型(包括4个异常级别:EL0 - 用户级别,EL1 - 内核级别,EL2 - 虚拟机监控器级别,EL3 - 安全监控器级别),基于更大寄存器文件的新A64指令集,以及新的FP/SIMD指令。新的ABI采用LP64模型,并利用了更大的寄存器文件,同时要求使用浮点指令。
目前已经公开提供AArch64文档。建议查阅官方ARM文档和资源,以获取关于AArch64及其规范的全面信息。
- Instruction Set Overview
- ABI (PCS, ELF, DWARF, C++)
AArch64 Linux端口遵循了对于新架构端口的指导方针,使用通用的头文件(包括unistd.h)和尽可能多的通用代码(一些库函数可能会根据基准测试结果进行优化)。
目前尚未提供硬件平台。从内核的角度来看,目标是尽量减少(甚至完全删除)架构特定目录中的平台代码。目前强制使用FDT(平台描述树),并正在进行ACPI支持的讨论。
在内存管理单元(MMU)方面,它目前支持用户和内核各自的39位地址空间,采用3级页表和4KB页面或2级页表和64KB页面(详见Documentation/aarch64/memory.txt)。虚拟地址空间可以扩展到48位。
兼容性(32位)用户应用程序(仅限ARM EABI)在4KB页面配置下得到支持。AArch32和AArch64之间没有相互操作(该架构需要通过异常进入/退出来更改模式)。
在开发过程中,使用Linux测试项目(LTP)和LAMP堆栈对这些代码进行了测试和验证,并针对ARM仿真模型进行了测试。编译需要一个新的aarch64-none-linux-gnu工具链。
四、64位ARM独立实现
这是迄今为止在我们之前的私人审查和公开的Linaro会议中最有争议的问题。你说得对,除了你提到的架构之外,其他人都合并了它们的架构(还有sparc、mips,如果我没记错,parisc和tile总是合并在一起)。
我在回复Olof时也提到了这个问题,但我可以更详细地总结一下我们之前讨论的内容以及我的理解。基本论点是:
a)支持合并:避免代码重复,因为重复的代码会导致重复的错误和额外的维护负担。
b)支持分离:从一套全新的代码库开始,摆脱我们目前正在改进的所有旧代码;而且实际上大部分代码都是不同的。
我长时间对此持观望态度,并尝试从两个方面进行考虑,但现在我更倾向于b)。
看看架构树中实际存在的内容:
(1)汇编代码:在其他架构中,32位和64位指令集大部分是相同的,通常64位有额外的指令,但在ARM AArch64的情况下,指令集实际上是全新的,不能以一种同时适用于两个版本的方式编写汇编代码,A64指令集和A32指令集时不兼容的,是两套完全不一样的指令集,指令编码格式也是不一样的。
(2)系统调用接口:大多数架构在32位和64位上使用相同的用户空间ABI,只有细微的差异。在AArch64的情况下,我们使用了新的通用ABI,而32位ARM使用了长时间发展并带有许多旧代码包袱的ABI。AArch64必须模拟32位ABI以运行旧的用户空间,但32位兼容实现实际上无法与本机32位实现共享任何代码。
(3)平台支持:大多数架构都有一个单独的系统级架构,描述中断控制器、定时器、SMP初始化等内容,通常在32位和64位之间共享。在32位ARM上,我们有大约50个不同的平台,但其中一些与其他架构的平台有很多共同之处(arm/shmobile与sh、arm/imx与powerpc/85xx、arm/rpc与x86等)。我们目前正在重新处理的就是相同的代码,并将其从arch/arm移出,放入驱动程序中,只要有意义。
(4)基础设施:PCI支持、ptrace、信号处理、DMA映射等。许多在架构之间复制的东西实际上是完全通用的,应该将其移入公共代码中,而不是arch/目录结构中。与其仅在32位和64位ARM之间共享,不如在aarch64和其他多个新架构之间共享。
考虑到我们不打算共享的所有这些内容,我认为最好是对新代码保持灵活,并清理或尝试一些在合并的代码库中很难改变的东西,而不会出现为32位系统引入许多回归的风险。重复代码的风险仍然存在,但我认为我们可以根据具体情况处理,并确保不合并应该以某种形式共享的重复文件,或者应该以不同方式开始处理的文件。
与从IA64到x86的转变相比,ARMv7到AArch64之间存在着显著的差异。IA64在很多方面与x86完全不同,而AMD则更直接地实现了向后兼容,市场也有所反应。而在这种情况下,由于多种不同的原因,情况有所不同:ARMv8处理器也能很好地支持ARMv7(尽管一些很少被使用的功能被弃用,但仍然受支持),现有市场的组成情况(ARM在桌面或服务器上不需要30年的向后兼容故事,移动平台已经发展到更高级别的技术堆栈),许可模型的动态性,以及编译代码的重要性较低。因此,我建议不要过多地将其与x86/IA64/x86_64进行比较。
AArch64对于ARM来说并不是完全的改变。它是一个32位宽的精简指令集(RISC),借鉴了过去几十年的行业经验,修复了A32/T32(ARM)中存在的许多问题(据我所知)。在ARMv7中,他们已经在废弃旧特性方面做了很多工作,但从实现的角度来看,有机会去除那些不再有意义的东西,比如许多条件指令,通过对实际软件进行建模来优化编码格式等,并清理掉十年前关于流水线深度的显式知识。
我非常看好这里的机会,例如使用单个zImage和一套标准化平台。到第一批硬件出货时,我希望我们已经有了一套良好的标准,供所有人遵循,这些标准是在更大规模系统中进行AArch64所需的所有内容。
参考资料
https://lwn.net/Articles/506148/
https://zhuanlan.zhihu.com/p/659139628
这篇关于Supporting 64-bit ARM systems的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!