UEFI与硬件初始化

UEFI与硬件初始化

UEFI论坛的PI 规范允许使用模块化的方法支持芯片和硬件平台的初始化。这其中包括:系统初始化,引导,以及引导前的特殊配置需求。

在BIOS领域采用UEFI规范后的巨大影响是有目共睹的。如今,UEFI已是固件,操作系统,附加设备和其他行业标准的必备组件。基于Intel架构的PC生态系统中的大多数产品都是基于原先的EFI规范或其继任者:UEFI规范,或是PI规范。这些规范已成为X86架构的PC生态系统中硬件初始化的基石。

本文将探讨如何应用UEFI平台初始化(PI)规范作为使能芯片和硬件平台的框架。我们将学习和了解规范提供的构建块元素,还会对平台启动过程,独特模式和常见用途做一些介绍。此外,我们还将检视各类处理器,内存和图形控制器以及支持芯片的驱动程序。

介绍与回顾

在过去的十年中,PC悄然经历了一场由芯片使能化推动的革命。行业从单一的PC演化成为采用多种架构的不同产品线,引入了可扩展的和面向组件的工业标准固件架构 ,同时BIOS环境采纳了这些架构和规范。这场革命的开始有很多原因,但最主要的是由于需要以具有成本效益的方式初始化和使用日益复杂的芯片和产品。

定义:何谓芯片使能化?实则英文原词为silicon enabling,硅使能?如何理解呢?其实不复杂,首先强调一点,公司的存在当然是为了盈利的。又有个比方说,假如你买了一堆积木,先想了下要搭个什么东西,然后你就通过各种方法把他搭成了你预想的样子。芯片(Silicon)制造商做的事情其实差不多,首先,他要购入硅,塑料,各种金属(买积木),通过技术把这些东西有机组合在一起,使其“变废为宝“(按自己想法搭建),变成了具有各种用途的芯片,然后宣传其特点,试图卖出这些商品来获利(展示给别人看)。这些商品也和其他常见商品一样,具有性状,使用方法等特点,也随商品附赠使用手册,只不过这里的使用手册的内容包括的是:芯片规范,参考代码,二进制模块,默认设置等较专业内容。而OEM的责任就是使用这些芯片搭建出终端用户使用的产品。概括的说:silicon enabling指代的是OEM如何使用芯片,从无到有制造产品,开发/使用/配套驱动到整个产品可用的过程。读者应理解,本文译作芯片使能化并不完整,但可用来强调文章解释的重点在于芯片的软硬件初始化实现,而不包括原意中芯片的硬件制造阶段。

不同的需求催生了行业标准的制定。但即便在有明确需求的情况下,行业标准也不仅仅是写入文件就算制定成功的。UEFI论坛所制定的PI架构之所以成为“广泛接受和认可的可扩展工业标准架构”并作为一种标准,基于以下五个原因:

它被写入UEFI平台初始化规范中。提供了二进制可互操作组件架构,其主要作用是在加载OS之前进行芯片初始化。它通常被认为是一个BIOS架构规范。

•它在两个开源软件开发环境(EFI开发套件(EDK)和EDK II)中得到实现; 这些工具都可在Build software better, together上找到。这些工具包提供了可用性强,易重复使用的实现方式,模块开发商可以作为参考,来构建各种符合UEFI和PI规范的产品。

•它在BIOS产品中被实例化为事实上的标准实现,通过被称为Intel Green H的概念(包含工业标准接口的头文件等)。 只需要最小程度的移植,就可以在多个多个BIOS代码库中的使用。

•它可以使用UEFI发布的自认证测试(Self-Certification Test,简称SCT)进行测试。 SCT会检查各种UEFI规范中定义的接口的实现是否符合标准。

•他被行业领先的硬件,软件,系统生产商应用,因而市场需求量很高。

•提供了大量的平台和芯片参考代码实现,譬如CPU,SIO,开放硬件平台(Minnow系列和Galilo)。

作为一个BIOS软件标准,必须要符合这样一个特殊要求:必须能支持多种不同活动。我们在此重点讨论其中两个:硬件初始化和系统调试。它涉及到源代码和二进制模块的兼容性和互操作性。 通过符合该要求以便有效地实现面向组件的架构,该架构由整个行业中支持Intel架构的PC生态系统的众多公司使用。以我们之前讲过的PI为例,二进制的兼容性由规范文档和测试(SCT)保证,源代码兼容性通过EDK和Intel Green H实现。靠着全行业内BIOS,硬件,和软件供应商的支持,PI得到了全面普及。

为了更好地探究如今的芯片Enabling支持环境,有必要重温PI定义本身,和基于它的可扩展的芯片初始化模型,以及可能的机遇。


PI的架构

什么是BIOS?名字是basic input/output system的缩写。BIOS的终极目标是初始化硬件平台并引导操作系统。

最早的PC/XT系统的BIOS只有8KB,也能启动DOS。那是在1982年,IBM发明了它。从那以后在经历了一段平静期后,BIOS得到了飞速进化,尤其在UEFI诞生后。

我们把最初的BIOS称为传统BIOS,称UEFI类引导代码为UEFI,不论是哪一样,一些重要的职责都是不变的:首先,二者都有平台初始化的重要使命。这一过程其实是在平台重启(s3,s5及其他)之后立即执行的一段代码。传统BIOS中,这是供应商特定的流程和结构,但很多时候只是些不需要堆栈的汇编代码和启动块,不同的BIOS厂商(IBV)直接的代码差距很大,不具备互操作性,规范性很低。而在UEFI中,针对这一过程,提出了专门的平台初始化标准(UEFI PI Spec)。在基于PI的平台初始化中,SEC和PEI阶段肩负前期初始化的责任。基于UEFI PI的引导的时序关系如下图所示:


之后,每个硬件平台都需要发现I / O总线,从主机总线适配器调度opROM(option ROM)等等。 在传统BIOS中,此I / O枚举发生在上电自检(Power On Self Test,简称POST)阶段。在常规BIOS上没有POST的真正标准。 但是,对于基于UEFI PI的固件,此阶段的执行发生在驱动程序执行环境(DXE)中。DXE还可以作为UEFI核心,用于支持基于UEFI的操作系统。

在BIOS POST和DXE之后,就到了标准描述的平台接口部分。对于PC / AT BIOS,该标准包括在x86架构上以16位实模式执行的事实上的中断可调用接口(例如,磁盘的Int13h,视频的Int10h)。对于UEFI,则是UEFI引导服务和协议protocol(例如,EFI_BLOCK_IO_PROTOCOL与BIOS int13h类似),由UEFI规范对此进行详细说明。在该阶段执行期间,第三方驱动可以从磁盘或外置适配器中读取而未必由平台制造商(PM)系统板提供。

上文所述的二者区别至关重要,因为从一个抽象的,由PM提供的执行规则转变成为具体的,第三方代码可以运行的空间,这一实现的质量对构建可信平台有很大的影响。

下图展示了通用BIOS初始化流程。 此流程包括在POST期间平台CPU,内存和I / O设备的初始化。 POST流程类似于前图中的DXE流程。


UEFI固件

在硬件层之上,OS之下的安全架构就是固件,如下图:


由于目前和将来可能有平台固件的若干种代码的实现,因此本文将对平台初始化(PI)标准及其一些代表性的代码库进行讨论。

作为背景,UEFI论坛的PI工作组——PIWG提供基于英特尔PEI和DXE规范的PI架构规范。PI体系结构规范于UEFI 2.0规范中独立出来。PI架构平台仍然可以启动现今的操作系统。AMD, AMI, Apple, Dell, HP, IBM, Insyde, Intel, Lenovo, Microsoft, and Phoenix共同拥有这一规范。PIWG的任务是允许当前创建了“参考代码”的芯片供应商将此参考代码打包为嵌入PI架构固件实现的模块。下图说明了PI范围。


UEFI和PI规范仅作为阐述关于接口定义和机制的规范材料,但是没有诸如为什么这么做和如何做的具体内容。 后者(PI)仅作为设计指南。

与传统BIOS的单一目的性相反,UEFI PI通过阐释驱动程序模型(例如基于依赖性表达式的PEI模块(PEIM)和DXE驱动程序)所带来的软件可扩展性来实现硬件敏捷性。 可扩展性点也可能被作为恶意软件的攻击点。这类恶意软件基本上不能被OS所拥有的防护技术(例如杀毒软件)检测到,因为恶意软件可以在OS之前就被完整执行。因此,基于UEFI的pre-OS引导环境以及其他的pre-OS可扩展性的安全完整性基础必须确保稳固,同时还要提供足够的灵活性来支持硬件敏捷性。

PI阶段旨在仅由平台制造商(PM)而不是第三方(与UEFI及其选项ROM /加载程序/驱动程序模型相反)可扩展。 在这个PI过程中,代码的安装和其行为是在平台制造商的授权下进行的; 下文中简称为“PM_AUTH”。让PM才能扩展PI,保证了系统的安全性。

下图描述了UEFI PI引导流程,包括仅PM可扩展的PI代码和第三方可扩展UEFI。


芯片初始化

UEFI PI提供了一个软件环境,允许芯片供应商提供几乎所有必需的核心芯片初始化。为了本讨论的目的,我们不区分UEFI和PI这两组规范,因为二者的应用范围中有太多类似的用于组件开发及其互操作性的基础架构。

对于硬件自我初始化,软件初始化硬件相对而言成本更低和灵活性更好,所以仍然是必要的。 在过去20年中,我们看到处理器从10万晶体管增长到1000万个以上。这种增长,带来了一系列愈发复杂的特征和能力。 而软件仍然是初始化和支持这种越来越复杂的硬件的最有效的方法。

出于本文的主题考虑,我们关注的是构建X86架构系统的关键组件(核心芯片):处理器,内存控制器,图形控制器,存储控制器,系统总线控制器,IO控制器等。PC中当然还有许多额外的设备,但他们也可以从本文中类推而得,不在详述。此外,这些设备往往在引导过程中的稍后时间被初始化,更多地依赖于其他工业标准,如USB,PCIE和ACPI。在某些情况下,BIOS在其初始化他们中起到的作用并不显著。

1。芯片初始化发生在哪?

核心芯片初始化依赖于分阶段的方法。在初始系统复位时,仅有部分非常有限的硬件资源可用;不能访问设备,不能访问存储器等等限制。这才导致了随着硬件的初始化以及可用资源的增加,BIOS为软件的正常运行不断地改变操作环境。在PI中:

• 在SEC阶段,系统从复位开始运行(由主机引导处理器取回的第一指令),通过初始化处理器高速缓存(Cache)来作为临时内存使用,我们有了堆栈,从而可以执行c程序,然后转到PEI阶段。

• 在PEI最开始阶段,仅少量栈和堆可用,我们需要找到并使能足够我们使用的永久内存,通常是内存颗粒或内存条(DIMM),然后转入DXE。

• DXE阶段有了永久存储空间,真正开始负责初始化核心芯片,然后转换到BDS阶段。

• 核心芯片初始化完成后BDS阶段开始,并继续初始化引导操作系统(输入,输出和存储设备)所需的硬件。 纵观PI的整个阶段,BDS对应的是“执行UEFI驱动程序模型”来引导OS这一过程。

流程中有几个特殊的子阶段:

• PEI pre-mem

• PEI post-mem

• SMM

• CSM


如上表所示,芯片初始化遍布整个引导过程。之所以这样设计,而不将初始化内容放在一起完成,主要是基于成本和复杂性的考虑:随着引导的进行,有更多的基础架构可用,初始化一组芯片功能模块的成本和复杂性下降。这不是说SMM初始化过程不复杂,而是说,在永久内存和DXE服务可用后,其后阶段的复杂性虽仍然维持在“复杂”一级,但的确不如之前过程复杂。这里所说的“成本”,说的是要在存储器可用前,从未压缩的FLASH存储器直接执行程序(XIP)。最终结果就是,在表中列举的大多数核心芯片初始化活动可以由PI模块合理且廉价地实现,完成度通常能占到全部任务的90%以上。

2。芯片初始化如何实现?

我们以Intel的方案为例,芯片初始化代码以芯片对应的程序包(Package)的形式提供。这些封装包可以支持单个产品,多个产品,与单个产品相关联的类似产品,甚至可能支持跨越多种产品。样例包括平台电源管理(platform power mamagement,简称PPM)参考包(其通常支持多代芯片),南桥芯片(platform controller hub,简称PCH)参考包(通常只支持对应一代的芯片)和集成时钟控制器(integrated clock controller,简称ICC)参考包 ,ICC仅在PCH用于特定配置时才适用。对象模型的复杂性源于整个平台的复杂性以及参考包使用的灵活性。PPM参考包通常在产品开发周期的后期启用,因此,将其与“总是必需的”处理器代码分开封装,为客户提供了产品开发的灵活性。

每个包含有各种阶段的驱动程序,例如memory之前的PEIM(pre-memory,PEIM),UEFI引导服务以及运行时服务(DXE),系统管理模式(system management mode,简称SMM)驱动以及其他。这些DLL文件通常以源形式,在常规芯片参考包中递送,一般包含:

1.源代码

2.自定义接口及其文档

3.用于在开源EDK/EDKII中使用的构造文件。

4.示例代码

5.静态库(static libraries)

6.设计和移植文档

为了减少支持的配置数量,针对特定版本的工业标准接口和实用库,开发对应的芯片参考包并通过测验,即Intel的Green H计划。该计划的意义在于,通过封装特定的一组文件,使得提供具有易集成和可重用性的源代码而不无需修改变得可行。而经过业内讨论,一致同意在头文件中包含服务协议调用表(services tables)及公共协议(Common protocol)定义,如此一来就避免了大量的源代码可移植性问题。

尽管PI提供了丰富的基础服务类型,甚至有时候,他的能力显得有些多余,但是,用工业标准接口来覆盖所有可能的芯片特性或许仍力有不逮。因此,参考包还提供了自定义接口。这些接口通常采取变量、HOB、PPI、Protocl,以及依赖关系(dependency)、回调和其他UEFI和PI类型的服务。利用这个“工具箱”,芯片厂家可以为“难搞”的芯片初始化提供丰富的“服务”。在当前的芯片参考封装包中,通常还有提供一组设置接口“Policy”,允许使用者根据主板或者设计的不同来定制某些参数。所有这些既保证了芯片包的独立性,又提供了很高的灵活性,方便OEM使用。

总的来说,使用提供代码的芯片参考包并采用Intel Green H,OEM等使用者可以应用广泛可用的EDK/EDKII构建模块,他们只需关注如何将这些模块集成到他们的BIOS构建环境并在其BIOS中发挥芯片参考封装包的各种特性。如此一来,芯片供应商便可以提供在碎片化的环境中也能重复使用的芯片初始化实现代码,而OEM等也可以专注于自己的核心业务。

BIOS角色的转变

UEFI PI标准的出现成为了行业的分水岭,从此BIOS相关各方的角色也发生了不可逆转的变化。

1。实现一次即可

PI允许芯片供应商开发芯片初始化代码,并使其用在各种场合下可用。以前,诸多公司各自基于所用芯片(said silicon,简称SI)的有限文档来进行核心芯片初始化工作。这样做的明显缺点就是SI消费者在芯片上实现的初始化可能与芯片制造商的原本意图大相径庭。此外,SI生产商虽负责初始化代码的实现和验证,但对SI消费者而言它却要自己开发,其中出现了严重的脱节。显然由PI带来的芯片初始化代码包可以一次开发,各处使用,保证了信息的连贯和准确。

2。部署

为避免干扰敏感的模拟信号量,也出于总体布局的考虑,越来越多的SI消费者将系统板设计和SI生产商紧密联系起来,更多地考虑在SI初始代码上做文章。正如上文提到的,SI生产商生产SI初始化代码以在内部验证SI。如果SI生产方和使用方都为其系统板固件支持了UEFI PI规范,SI生产方可发布其SI初始化模块,同时发布SI物理器件。这样,就可以在采用的部署模型中实现“硬件和固件”的无延时。

3。集成

支持UEFI和UEFI PI规范的系统的出现亦改变了PC固件生态系统中各个玩家的关系和责任。

在以前(UEFI和PI出现之前),芯片供应商提供参考源代码来描述用于初始化其特定芯片或芯片组的重要初始化和配置步骤。BIOS供应商使用该代码,对其进行修改后用到他们的代码库中,并将每个参考代码段链接到它们的程序中。为所有客户提供BUG修复服务费时费力费资本,因为每个客户都经历了鬼才知道的步骤形成了自己独特的目标代码库,旁人维护何其艰难(这正是没有规范的时候的行业困境)。BIOS的质量保证依赖于功能测试或自主开发的API测试,因为(当时)很少有明确定义的API。扩展BIOS的唯一标准方法是可选ROM,20多年来基本没有什么变化(从BIOS的诞生到UEFI诞生之间的时间,相较于PC性能的日新月异,这个东西一成不变显得多么不合群)。

随时间的推移,许多芯片供应商开始为他们的产品研发“核心”源代码包,并为每个他们支持的BIOS代码库研发“插件”层。这种方案在当时确实缓解了芯片供应商面临的一些问题。因为现在,他们只需要钻研一个问题,做出一点改变:就是使“核心”可以工作在所有的目标代码库。但也对BIOS供应商提出一个新的难题。因为每个芯片供应商都在各自研发这样一个“插件层”,每家公司都有自己的BIOS代码库,有不同的芯片供应渠道。代码的通用性其实还是没有得到根本改善。

随着UEFI和后来PI的推行,行业整体前进了一大步。现在每一个芯片供应商可以封装支持他们的芯片驱动程序。规范定义了它们是如何启动的,如何发布接口,以及如何发现接口的。因为标准化,不同的BIOS厂商代码库不再有千差万别的“插件”层。使其再也不必被每个客户(BIOS供应商或OEM)“奴役”(一家一本天书,说奴役还真不是夸张)。

这种新模式产生了一系列影响:

1. 一旦写成,随处可用。如果BIOS环境符合规范,符合规范的驱动可以插入到任何BIOS和他应该发挥作用的地方。

2. 芯片供应商生产驱动,随着UEFI应用时间愈久,与芯片支持相关的所有源代码的任何问题都会由芯片供应商负责。

3. 可测试性的提高。UEFI为启动系统所需的一切服务都提供了明确定义的API,这些API实现的鲁棒性(rubustness,来源于rubust,强壮,健壮的意思,计算机的鲁棒性一般指可观察性,可恢复性,响应性,任务规范性)对于确保互操作性至关重要。为了能充分发挥这些API的优点,UTWG(UEFI测试工作组)为每一版的规范制定了SCT。

4. 孤立到模块化BIOS。BIOS已越来越多地作为一个平台,使众多驱动和应用得以良好运行,而不再是一堆代码体的杂乱堆放。

5. 驱动提供内置的可定制化。在以前的模型中,为特定平台或产品的定制化是由OEM或BIOS供应商在开发周期的后期进行处理,而且经常涉及直接修改供应商所提供代码的操作。如今芯片供应商通过采用PI 标准的平台配置数据库(Platform Configuration Database,简称PCD),策略协议以及EFI变量,在驱动中直接实现可定制化。

BIOS世界的内部工作原理一经公共规范揭露和阐释,许多新的灵感就会纷至沓来。UEFI使得有一定代码基础的人都有可能进行底层的开发,相较于老旧的BIOS的晦涩,复杂,UEFI着实简单了太多,何况还有规范的EDK供你使用,任何有想法的人都可以在自己的PC上进行验证,正所谓众人拾柴火焰高,UEFI BIOS功能增长之快似乎也不那么让人惊讶了。

互操作性例子

UEFI PI规范并没有给你构建一个PC平台所需的一切,许多小型芯片组件——嵌入式控制器,超级I/O控制器(super I/O,也叫做I/O芯片,从486以后的主板开始采用,在南桥这样的高速设备和串行、并行接口、软盘驱动器及键盘鼠标等大量低速设备之间必定存在资源的不匹配,而需要经过转换和管理。Super I/O芯片则完成了该功能),OTG设备等等需要自己开发。然而,PI规范提供了可以构建质量可靠的固件的坚实基础(虽然没有给你砖瓦,但却给了你土,水,和火)。

我们以PI规范中对系统管理总线(System Management Bus,简称SMBus)的支持为例,做一个比较详细的表述。这东西本来是为支持电池管理子系统而开发的,这种双线多主式总线接口已发展成用于传感器和平台管理的标准主板边带总线。自1995年推出以来,它已成为其他行业标准的一个组成部分,包括PCI,IPMI,DASH以及ASF。

规范中描述了两个描述SMBus 主控制器的低级特性的API:EFI_PEI_SMBUS2_PPI (用于PEI阶段)和 EFI_SMBUS_HC_PROTOCOL (用于DXE阶段)。这些API允许在SMBus地址上发送和接收命令,而无需对硬件接口进行专门的学习:

typedef struct _EFI_SMBUS_HC_PROTOCOL {
EFI_SMBUS_HC_EXECUTE_OPERATION Execute;
EFI_SMBUS_HC_PROTOCOL_ARP_DEVICE ArpDevice;
EFI_SMBUS_HC_PROTOCOL_GET_ARP_MAP GetArpMap;
EFI_SMBUS_HC_PROTOCOL_NOTIFY Notify;
} EFI_SMBUS_HC_PROTOCOL;

这个协议接口结构具有到不同抽象函数的函数指针。Execute 会通过SMBus向目标SMBus设备发送指令。ArpDevice和GetArpMap将处理SMBus地址解析协议,为SMBus设备分配唯一的地址并报告结果。当SMBus设备发送事件通知时,Notify允许其他驱动程序注册回调。

操作者无需了解SMBus主控制器在硬件中如何实现,实际上,他都不需要知道硬件是不是真的存在。就这样,只要Green H中定义了接口规范,芯片供应商在芯片参考代码包中包含了其实现,OEM或IBV即可直接调用而不必考虑过多细节,互操作性得到极大提高。

后记

UEFI PI的出现,使得固件开发也可以像点菜一样,规划好菜式,根据口味,一项一项组合,再配上一些自己的秘方即可。从此,BIOS工程师过上了幸福的生活(不可能。。)。

UEFI的拼图中有一块重要的部分:UEFI驱动模型(UEFI driver model),我们会在有机会的单独介绍。

UEFI历史和架构其他文章:

UEFI和UEFI论坛 - 知乎专栏

UEFI背后的历史 - 知乎专栏

ACPI与UEFI - 知乎专栏

UEFI安全启动 - 知乎专栏

UEFI与硬件初始化 - 知乎专栏

UEFI架构 - 知乎专栏

欢迎大家关注本专栏和用微信扫描下方二维码加入微信公众号"UEFIBlog",在那里有最新的文章。同时欢迎大家给本专栏和公众号投稿!

用微信扫描二维码加入UEFIBlog公众号
编辑于 2017-10-28

文章被以下专栏收录