基于ARMv8的固件系统架构

自 2011 年发布以来,ARMv8 处理器架构在移动设备市场上已经相当普遍。根据ARM Limited CEO的预测,到2020年,这一代处理器的全球市场份额将达到25%。历史形成的基础设施的原则。

在市场的服务器部分观察到一种根本不同的情况。基于 X86 的服务器在这个领域占据主导地位已经有一段时间了,而 ARMv8 才刚刚找到它的出路(并且只进入特定的业务领域)。ARM 市场的新颖性以及大多数公认的标准和规范(主要是 ACPI 和 UEFI)直到最近才适用于 ARM 系统这一事实已经在软件基础设施的开发上留下了印记。

本文重点概述基于 ARM 的服务器系统和处理器功能,并不声称是详尽的描述。作者还想提请读者注意这样一个事实,即所提供的数据可能很快就会过时——很快,新处理器将带有新的技术解决方案,这些解决方案可能需要采用不同的方法来实现软件基础架构。

首先,我们应该指出,当前 ARMv8 服务器系统固件的实现由几个相对独立的组件组成。这提供了许多优势,例如可以在服务器和嵌入式系统的固件中使用相同的组件,以及引入更改的相对独立性。

那么,这些系统中使用了哪些模块和组件,它们的功能是什么?模块加载和交互的总体图表如图 1 所示。该过程从子系统的初始化开始,例如 RAM 和处理器间接口。在当前的实现中,这是在打开主 CPU 电源后立即由处于 EL3S 模式的单独模块执行的。因此,系统的这个组件具有最大可能的特权。它通常不直接与操作系统交互。
基于ARMv8的固件系统架构_第1张图片随后,控制权转移到下一个组件,最常见的是 ARM 可信固件 (ATF) 模块,该模块以相同的模式执行。ATF 控制可以直接从上一段中描述的 0 级加载程序传输,也可以通过实现 PEI(PreEFI 初始化)的特殊 UEFI 模块间接传输。ATF 由多个模块组成,这些模块在不同时间接收控制。

BL1 启动模块执行分配给安全处理器模式的平台部件的初始化。由于基于 ARMv8 的系统对可信和非可信资源(包括 RAM)使用硬件分离,BL1 模块准备了一个可以执行可信代码的环境。特别是,这种类型的初始化包括内存/缓存控制器的配置(可信和非可信区域通过这些设备中的寄存器编程来标记)和片上设备(与能源无关的内存控制器)的标记。此标记还引入了基于设备类型(受信任/非受信任)的 DMA 事务过滤。鉴于所有这些,内存写入/读取只能到/从安全设置与设备匹配的区域进行。可信环境的实现可能非常复杂;例如,它们可以包含一个单独的操作系统。但是,对此类实现的描述超出了本文的范围。

BL1 模块配置 MMU 地址转换表以及异常处理程序表,其中最重要的元素是安全监视器调用 (SMC) 指令的异常处理程序。此时,处理程序是最小的,实际上只能将控制权转移到加载到 RAM 中的图像上。在运行时,BL1 模块将下一阶段 (BL2) 加载到 RAM 中并将控制权转移给它。BL2 模块工作在 EL1S 模式下,权限降低。因此,使用“ERET”指令执行对该模块的控制转移。

BL2 模块的目的是加载剩余的固件模块(BL3 部分)并将控制权转移给它们。降低的特权级别用于避免可能损坏内存中已有的代码和 EL3S 数据。这些部分的代码是通过使用 SMC 指令调用位于 BL1 阶段的 EL3S 代码来执行的。

ATF 加载和初始化的第三阶段可以由三个阶段组成,但第二阶段通常被省略。因此,实际上只剩下两个。BL3-1 模块是通用软件(操作系统等)在运行时可访问的可信代码的一部分。该模块的关键部分是“SMC”指令调用的异常处理程序。模块本身有用于实现标准 SMC 调用的函数:实现标准 PSCI 接口的代码(旨在控制整个平台,例如启用/禁用处理器内核、平台范围的电源管理和重新启动)并且还处理供应商-特定调用(提供有关平台的信息、管理嵌入式设备等)。

如上所述,BL3-2 模块的存在是可选的;它的代码(在模块的情况下)在 EL1S 模式下执行。通常,它充当平台操作期间发生的事件(来自某些计时器、设备等的中断)的专门服务/监视器。

实际上,BL3-3 不是 ATF 模块,而是在非安全模式下执行的固件映像。它通常在 EL2 模式下进行控制,并代表类似于广为人知的 U-Boot 的引导加载程序或 UEFI 环境的映像,这是服务器系统的标准。

ATF模块初始化的总体图如图2所示。

图 2. ATF 模块初始化。(来源:御夫座)

在某些基于 ARMv8 的服务器系统中可能会使用另一个初始化路径:ATF 在 UEFI PEI 阶段启动,之后转换到 UEFI DXE 阶段。

ARMv8 UEFI 与 x86 上的有很大不同。PEI 和 DXE(驱动程序)阶段用于 x86 和 ARMv8。但是,在许多 ARMv8 系统上,PEI 阶段显着减少,并且在此期间不执行硬件初始化。此阶段包括设置 MMU 转换表、配置中断控制器和 CPU 计时器(根据 UEFI 规范,此环境处理的唯一中断是计时器中断)、构建 EFI 切换块 (HOB)、和 DXE 核心的执行。在此阶段,本机 UEFI 模块倾向于使用上述特定于平台的 SMC 调用。

大部分 UEFI 工作在 DXE 阶段执行。首先,这涉及硬件驱动程序的加载和启动——包括通过 PCIe、USB、SATA 等接口连接的片上外围设备和外部设备。

需要注意的是,基于ARMv8的系统在配置、设备检测机制等方面与基于x86架构的类似系统有很大的不同。例如,x86的主要设备检测机制是扫描PCI配置空间和分配内存地址到设备,他们必须解码。在基于 ARMv8 的系统的情况下,内置外设几乎总是在内存空间中具有固定地址(端口未使用,因为它们不受 CPU 架构支持)并且在某些情况下在 PCI 配置空间中不可见。对于这样的系统,有一个由扁平设备树组成的硬件描述,设备连接的树状描述,它还描述了与这些设备相关的内存范围和中断号等资源。

在更高级的系统中,SoC 支持通过 PCI 配置空间进行访问,并包含通过增强型配置访问机制 (ECAM) 实现对该空间的访问的控制器。由于这些单元的内存地址是固定的,所以通用的PCI设备配置机制并不适用。具体来说,针对PCI设备地址窗口固定的系统,开发了Enhanced Allocation PCI能力,解决了这个矛盾。可以单独写一篇关于此功能的独特属性的文章。简而言之,它可以被描述为一组替代寄存器,其中包含有关内存地址、总线编号(用于内置 PCI-PCI 桥接器)等信息。

UEFI 不能与另一种传递平台配置信息的方法分开——ACPI。目前,用于改进对 ARMv8 架构的支持的 ACPI 规范的开发和改进正在进行中。根据现有信息,ACPI 应该成为描述平台基本信息(主要是处理器内核的数量和配置、PCI/PCIe 控制器)及其管理的关键方法。一些计划发布的 ARMv8 操作系统仅支持 ACPI 机制。

DXE 阶段包括设备检测及其在 UEFI 中的初始化和注册,以及为操作系统启动做准备。后者包括准备系统内存映射和配置信息——即加载、生成和发布 ACPI 表,修改这些表以反映平台的当前配置,对 FDT 进行类似的更改,以及检查和生成校验和. 在此阶段加载的模块可以实现 UEFI 运行时服务 - 可在运行时从操作系统调用的功能。需要注意的是,在本文作者曾经使用过的所有系统中,设备检测都是通过 PCI ECAM 机制实现的。

此阶段完成后,启动设备选择 (BDS) 开始。在此阶段通常使用一个单独的模块来处理“BootOrder”、“BootNext”和其他相关变量的值。通常,该模块实现(伪)图形用户界面。在这一点上,基于 x86 的系统有许多共性:使用相同的引导方法——PXE、iSCSI、块设备(如 SATA/SAS/USB 驱动器、SSD 和 NVME 设备)等。

有必要提醒读者注意ARMv8 UEFI的外部设备(通常是PCIe设备)的驱动程序。它们可以以位于存储设备(使用 FAT32 文件系统)中的模块的形式实现,也可以直接驻留在设备中(选项 ROM)。在某些情况下,将 ARMv8 添加到支持的体系结构列表会导致供应商出现问题。对 ARMv8 的源代码进行简单的重新编译并不总是足够的,因为某些模块并非设计为在完整的 64 位地址空间中运行。由于在 ARMv8 系统中广泛使用 PCI 总线到处理器地址的转换,反之亦然,因此也可能会出现困难。这是由于决定放弃位于内存地址空间的低 32 位内的传统“窗口”。在支持增强方面,在 EBC 字节码中编译的驱动程序可以提供所需的兼容性级别。但是,在撰写本文时,ARMv8 的 EBC 解释器还处于开发的早期阶段。

将控制权转移到加载到内存中的模块(引导加载程序或直接加载到 OS 内核中)是根据 UEFI 规范执行的:X0 寄存器中模块的 UEFI 句柄、X1 中的系统表指针以及X30 (LR) 中的返回地址。

操作系统内核使用 UEFI 服务执行一些准备步骤,然后设置自己的转换表并调用 UEFI 方法 ExitBootServices() 和 SetVirtualAddressMap()。这是必要的,因为 UEFI 代码与操作系统内核在相同的地址空间中执行。此外,必须禁用定时器中断和任何可能的 DMA 传输。ARMv8 Linux OS 设计有一个值得注意的方面:主要内核代码在 EL1 模式下执行,而 EL2 模式仅保留给部分 KVM 管理程序代码。因此,内核在初始化期间将其特权级别从 EL2 降至 EL1。之后,只有运行时服务(所有 UEFI 服务的一个子集)可供内核使用。如前所述,当在 ATF 模块之一中实现 PSCI 接口时,ARMv8 上的 Linux 内核广泛使用 PSCI 接口。这在多核系统中尤为突出。接口本身和二级CPU核心初始化的过程可以简单地描述为以PSCI函数号和初始化函数的入口点作为参数发出SMC调用。事实上,调用 UEFI 和 SMC 服务是当前操作系统和固件之间的主要交互方式。有其他固件事件通知工具的规范草案,但迄今为止(2015 年)还没有任何已完成实施的报告。调用 UEFI 和 SMC 服务是当前操作系统和固件之间的主要交互方式。有其他固件事件通知工具的规范草案,但迄今为止(2015 年)还没有任何已完成实施的报告。调用 UEFI 和 SMC 服务是当前操作系统和固件之间的主要交互方式。有其他固件事件通知工具的规范草案,但迄今为止(2015 年)还没有任何已完成实施的报告。

总而言之,应该提到的是,本文并未对基于 ARMv8 的固件组件的功能和交互进行详尽的描述。而且,架构的发展也在不断的细化和重构,很可能为新的文章提供素材。
相关实战:https://www.yunduoketang.com/article/xianshangjiaoxuext1.html
https://www.yunduoketang.com/article/k12jiaoxuxitong.html
https://www.yunduoketang.com/article/k12jiaoxuxitong.html
https://www.yunduoketang.com/article/k12jiaoxuxitong.html
https://www.yunduoketang.com/article/xiaochengxujiaoxue1.html

你可能感兴趣的:(嵌入式开发)