UEFI+BIOS全局配置数据库的设计与实现
扫描二维码
随时随地手机看文章
引言
BIOS是一组固化到计算机主板上一个ROM芯片中的程 序,它保存着计算机最重要的基本输入输出的程序、系统设 计信息、开机后自检程序和系统自启动程序。人们经常需要重 新对FLASH芯片进行编程以便升级BIOS,以便获得新的功能。
UEFI BIOS是目前最主流的一个BIOS架构,约占超过 70%的计算机,服务器和嵌入式市场。它是对老的BIOS开 发模式的一种彻底的革新,打破了 BIOS只能用汇编语言开发 和只能应用在计算机和服务器市场的局限性,当前越来越多的 嵌入式设备、平板、手持设备、工控设备、通信设备等都在 它的应用行列。
采用UEFI BIOS开发架构EDKII的架构,可保证同一份 核心代码运行在不同的硬件平台之上,仅仅需要针对平台的特 定性来设置一些特定的参数,这是EDKII架构中最难也是最 核心的一个设计部分,也就是要研究全局配置数据库PCD。
1 UEFI 和 EDKII 简介
2000 年,Intel 向 业界展 示了 BIOS 的 新 一 代 接口程序 EFI(Extensible FirmwareInterface),并将此技术应用于其安腾服务器平台上。EFI 是由Intel 推出的一种在未来的电脑系统中用来替代 BIOS 的升级方案。2005 年,在工业界达成共识的基础上,Intel 将 EFI 规范交给了一个由微软、AMD、惠普等公司共同参与的工业联盟进行管理,并将实现该规范的核心代码开源于网站上。与此同时,EFI 也正式更名为 UEFI(Unified Extensible FirmwareInterface)。UEFI 联盟将负责开发、管理和推广 UEFI 规范。
UEFI 定义了操作系统与系统硬件平台固件之间的开放接口。该规范定义的接口包括平台相关信息、启动服务例程以及操作系统运行时服务例程。操作系统装载器与操作系统可通过接口调用这些服务例程。UEFI 规范是一个公开的纯接口定义,它不依赖于某个特定的 BIOS 制造商或某个特定的 BIOS的实现,它仅仅定义了平台固件必须实现的接口,以及操作系统可能使用的一系列接口与数据结构,其实现的方式与细节均取决于该规范的实现者。UEFI 规范还定义了固件驱动程序模型,使得所有遵循此模型开发的固件驱动程序能够互相协作。
不同于传统的 BIOS 实现,EDKII 基于现代软件体系设计的思想,对 UEFI Framework 采用模块化设计,并根据其执行流程主要划分为 :SEC、PEI、DXE、BDS、TSL、RT 和AL 等 7 个阶段,其运行机理如图 1 所示。
SEC(Security)是平台上电后最先执行的步骤。这个阶段的主要目的是对平台固件进行验证,确保选择的平台固件映 像没有被破坏。主要工作是初始化临时内存区并对平台早期初 始化代码进行验证。
PEI (Pre-EFI Initialization)阶段有两个主要任务:确定 重新启动的来源和做尽可能少的工作以便寻找和初始化内存, 为DXE阶段提供少量的固定内存。
DXE (Driver Execution Environment)被设计来处理与外 围设备的通信,它通过加载驱动的方式(轮询检测)来为操作 系统的启动管理构建环境。
BDS (Boot Device Selection)是 UEFI 拥有平台控制权 的最后一个阶段。BDS与DXE阶段一起工作,为启动操作系 统建立控制台。
TSL (Transient System Load)即操作系统启动管理器尝 试引导操作系统的阶段。
RT (Run Time)是操作系统启动运行后,UEFI提供的 一组运行时服务。
AL (After Life)阶段,即最后一个阶段,其提供一种机 制来保证用户在有意或者无意的情况下终止操作系统后,让 UEFI重新获得系统控制权。
2全局配置数据库PCD的设计实现
EDKII中PCD根据其作用的时间,分两大类,一类 是在编译过程中起作用,这类PCD等同于C语言中的全局 静态变量,包含 FeatureFlag PCD,FixedAtBuild PCD 以及 PatchableInModule PCD三种。这类PCD跟全局配置数据库 没有关系,所以本文不做过多介绍。另一类是平台初始化过 程中起作用,包括 DynamicDefault PCD,DynamicHII PCD, 和DynamicVpd PC三种应用在源代码组件发布的PCD,以及 与之对应的 DynamicExDefault PCD,DynamicExHII PCD 和 DynamicExVpd PCD――专门应用在编译好的二进制组件发布 中的三种PCD。
2.1 PCD的分类和区别
从大面上,全局配置数据库中存放的PCD被分为两个大 类Dynamic和DynamicEx,每个大类又各分三个小类Default PCD,HII PCD 和 VPD PCD。
Dynamic和DynamicEx的作用局域完全一样,唯一的区 别就是源代码级别的发布还是编译好的代码发布。如果上层开 发者给二级开发者提供的是所有驱动的源代码,那么二级开 发者可以直接修改源代码来改变某个参数的值,此时只要把 该配置参数设置为Dynamic形式的即可满足要求。否则,必 须用DynamicEx的。DynamicEx的PCD在保护上层开发者 的版权和代码发布权限提供了更多层次的选择空间。
Default PCD :在初始化过程中,可以被PEI,DXE和 RT阶段的几乎所有驱动所使用,一般是前面的驱动修改,后 面的驱动读取。这是不同的驱动,不同的阶段之间有效信息交 互和传递的一种方法。该PCD的作用空间是一次加电过程, 所修改的数值在系统断电后会自动回复到默认初始状态。
HII PCD :作用空间和Default PCD 一样,主要的区别是 HII的PCD可以把修改的数值直接保存到BIOS NOR Flash芯 片的NVRAM区域。这样一旦修改,再计算机下次启动的时候, 访问的就是上次修改的新数值。
VPD PCD :作用空间和上面两种相同,主要区别是VPD PCD是只读的不能修改,但是它也有自己的优势。因为VPD PCD是的初始值是保存在BIOS固件的一段二进制数据空间 上的所以在固件编译完成后,可以在不依赖编译器重新编译 情况下,对该PCD的数值进行直接的重复设置。
2.2设计原理分析
在EDKII源代码编译中,编译工具集的AutoGen会 遍历整个平台所有驱动和顶层结构文件生成AutoGen.h和 AutoGen.c两个关键文件。这两个文件将作为后面C编译器的 自动包换的头文件输入,参与C语言的系统级编译过程,最 终生成这个平台的全局配置数据库。
下面通过一个NT32模拟平台中的例子来进行过程说明。
首先在NT32的顶层平台文件DEC, DSC和INF文件中 依次做如下声明。
MdeModulePkg.dec
[PcdsFixedAtBuild, PcdsDynamic, PcdsDynamicEx]
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariabl eBase|0x0|UINT32|0x30000001
Nt32Pkg.dsc
[PcdsDynamicExDefault.common.DEFAULT]
gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariabl eBase|0xf000
WinNtFlashMapPei.inf
[Pcd]
gEfiNt32PkgTokenSpaceGuid.PcdWinNtFlashNvStorageVariable
Base
通过该声明,定义了一个DynamicExDefault类型的 PCD,其类型为UINT32,初始默认数值为0xf000,并且驱 动模块WinNtFlashMapPei要使用该配置数据。
接着用EDKII的BaseTools对该源代码架构进行编译, AutoGen工具会在遍历完整个代码之后,在相应的PCD驱动 编译目录下面自动生成AutoGen.h和AutoGen.c两个文件,如下所示:
Aut oGe n.h (Bu ild \ NT32\ DEBUG_MYTOOLS\I A32\
MdeModulePkg\Universal\PCD\Pei\Pcd\DEBUG)
#define PEI_LOCAL_TOKEN_NUMBER_TABLE_SIZE 3
TOC \o "1-5" \h \z
typedef struct {
UINT32PcdFlashNvStorageVariableBase_a1aff049_
fdeb_442a_b320_13ab4cb72bbc[1];
DYNAMICEX_MAPPING ExMapTable[PEI_EXMAPPING_ TABLE_SIZE];
UINT32LocalTokenNumberTable[PEI_LOCAL_
TOKEN_NUMBER_TABLE_SIZE];
GUIDGuidTable[PEI_GUID_TABLE_SIZE];
UINT8StringTable[1] ; /* _ */
SIZE_INFO SizeTable[PEI_SIZE_TABLE_SIZE];
UINT8SkuIdTable[PEI_SKUID_TABLE_SIZE];
SKU_ID SystemSkuId ;
} PEI_PCD_DATABASE_INIT ;
AutoGen.c (Build\NT32\DEBUG_MYTOOLS\IA32\
MdeModulePkg\Universal\PCD\Pei\Pcd\DEBUG)
PEI_PCD_DATABASE_INIT gPEIPcdDbInit = {
{ OxfOOOU ), /* PcdFlashNvStorageVariableBase_a1aff049_ fdeb_442a_b320_13ab4cb72bbc[1] */
/* ExMapTable */ {{ 0x30000001U, 2U, 0U }, },
/* LocalTokenNumberTable */
{
offsetof( PEI_PCD_DATABASE , Uninit. PcdFlashNvStorageFtwSpareBase_a1aff049_fdeb_442a_ b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_ TYPE_UINT32,
offsetof ( PEI_PCD_DATABASE , Init. PcdFlashNvStorageVariableBase_a1aff049_fdeb_442a_ b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_ TYPE_UINT32,
},
/* GuidTable */
{{ 0xA1AFF049, OxFDEB, 0x442a, { 0xB3, 0x20, 0x13, 0xAB, 0x4C, 0xB7, 0x2B, 0xBC }}, },
//gEfiMdeModulePkgTokenSpaceGuid
/* StringTable */ /* SizeTable *//* SkuIdTable */
};
AutoGen.h自动生成的是该数据库的结构定义文件,它 定义了各个PCD在该数据库中存放的数据位置、类型、偏移 量等信息。AutoGen.c则配合AutoGen.h详细列出了各个比特 位置存放的具体数值。
当WinNtFlashMapPei驱动模块中想要访问该PCD数值
的时候,只需要在C语言中引用PcdGet32Ex (gEfiMdeModul ePkgTokenSpaceGuid, 0x30000001)。这时就会自动扫描 Init. ExMapTable和Init.GuidTable两张数据库表取得该PCD对应 的 LocalTokenNumber 数值"2U"。
而后根据映射LocalTokenNumber的数值 找 到“ offsetof ( PEI_PCD_DATABASE , Init. PcdFlashNvStorageVariableBase_a1aff049_fdeb_442a_ b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_ TYPE_UINT32"。
通过在 C 语言中解析 “offsetof (PEI_PCD_DATABASE, Init.PcdFlashNvStorageVariableBase_a1aff049_fdeb_442a_ b320_13ab4cb72bbc) | PCD_TYPE_DATA | PCD_DATUM_ TYPE_UINT32"的定义,就可以得到该PCD在数据库中的 偏移量、PCD类型和数据类型信息。
其过程如图 2 所示 :
3设计局限性和改进方法
在该数据库生成过程中,离不开对MS, ICC或者GCC 编译器的支持,这样DynamicEx所宣称的二进制固件发布模 式受到约束。换言之,想要完全的二进制驱动组件的发布,必 须让EDKII整个平台PCD的生成过程脱离对任何编译器的 依赖性。
针对这个设计要求,提出了新的设计架构,其流程图如图3 所示 :
在新的架构中,主要改变的是AutoGen的组件,EDKII 的编译工具集依然会遍历整个架构的所有驱动和上层配置 文件,但它会直接生成PCD数据库,同时生成一份包含改 数据库结构的AutoGen.h文件和一份空的只包含注释信息AutoGen.c 文件。AutoGen.h 和 AutoGen.c 依然采用 PEI/DXE驱动的源代码编译,只不过 PCD 数据库不在依赖该过程产生,因此稍加改动,就可使 DynamicEx 真正发挥其所宣称的作用。
4 结 语
EDKII 的 PCB 数据库目前在国内没有任何论文研究发表过,本文主要针对这个空白领域,分析和研究了 EDKII 最核心、最关键的全局数据设置数据库 PCD 的设计和实现,并指出了其设计的不足。随着 UEFI BIOS 的广泛应用,越来越多的军用板卡、通信主板、嵌入式设备和服务器也会转移到这个架构上。如果用一个稳定不变的核心代码来支持不同的设备,必然会减少维护成本,提高开发效率,以及提高设备的质量,这就是本文所研究的 PCD 技术的意义所在。
20211223_61c35c0e24e78__UEFI