udk开发-稀里糊涂

Posted 毛毛虫的爹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了udk开发-稀里糊涂相关的知识,希望对你有一定的参考价值。

一、EDK2简介
1.EDK2工作流

二、EDK2 Packages
1.Packages介绍
​ EDK2 Packages是一个容器,其中包含一组模块及模块的相关定义。每个Package是一个EDK2单元。

整个Project的源代码可以被分割成不同的Pkg。这样的设计不仅可以降低耦合性,还有利于分发和复用。

1.1.EDK2 Packages
​ 每个Package中包含一个DEC文件,该文件用来组织Package中的一系列Module。EDK2提供了一些兼容UEFI和PI的包(如MdePkg、MdeModulePkg等)。

​ MdeModulePkg包中有一组符合UEFI和PI规范的跨平台驱动程序。在开发新的UEFI和PI驱动时可以用来参考。

1.2.DEC文件
​ DEC文件用来定义每个Package中的公共接口:公共头文件、GUID、PCDs。

DEC文件主要包括以下部分:

(1)[Defines]

​ 定义PKG的名字和PKG的GUID

(2)[Includes]

​ 列出公共头文件的所处目录的根目录

(3)[LibraryClasses]

​ 列出在Include\\Library下的每个Library Class头文件

(4)[Guids]

​ 给Include\\Guid下的Guid赋值

(5)[Ppis]

​ 给Include\\Ppi下的Ppi赋Guid值

(6)[Protocols]

​ 给Include\\Protocol下的Protocol赋Guid值

(7)PCDs

​ 相关类型的PCD声明:FeatureFlag, FixedAtBuild, PatchableInModule, Dynamic, and DynamicEx。Dynamic类型的PCD在第四章:附录章节还有补充说明。

若一个PCD支持多种类型,则需要在全部类型中全部声明
DEC文件写法示例:

1.3.DSC文件
​ 编译时使用到的文件,所有模块均可加入来进行Package编译和验证。

DSC文件主要包括以下部分:

(1)[Defines]

​ 设置Build的相关信息,如build输出目录、build目的、Guid和build arch

(2)[LibraryClasses]

​ 对每个LibraryClasses指定选择的Library实例

(3)PCD

​ 用于配置[Components]部分中各模块使用的PCD类型和PCD值。

如果PCD的值与DEC中的默认值相同,且PCD类型没有具体要求,则将使用DEC中的默认值。
(4)[Components]

​ 列出所有将被编译的Module,如Drivers, Application, and Library Instances

Path是相对路径,相对于Package根目录

DSC文件写法示例:

2.Packages管理
2.1.创建Package
2.1.1.创建要求
(1)将有意义的包名作为包目录名,并创建一个包目录,例如PackageNamePkg

(2)在包根目录下创建包DEC和DSC文件来描述这个包

(3)创建子目录来包含不同的源文件

3.EDK II Module
​ 一个EDKII Module包含源文件或者二进制文件,和一个Module定义文件(inf文件)

3.1.Module组成
​ 对于一个典型的EDK II Module而言,Module是一个固件组件。Module在构建完成后,首先被放入FFS文件,然后放入FV Image。

​ Module可以以源代码或EFI二进制格式发布,Module的内容可以是下列几种类型:

(1)Raw data binary

例如:$(WORKSPACE)\\MdeModulePkg\\Logo\\Logo.inf ,这就是包含了一个logo bitmap的image

(2)可选的设备ROM驱动

(3)独立的UEFI驱动或者UEFI应用

(4)一个 .lib 形式的library instance

3.2.Module分类
​ EDK II给Module定义了许多不同的Module类型。定义这些Module类型主要是为了:

(1)区分不同类型模块的生命周期

​ 例如:PEIM在PEI阶段调度,DXE_DRIVER或uefi_driver在DXE阶段

(2)表示不同类型模块的二进制image

(3)为不同类型的模块指定合适的library instance

主要的EDKII Module类型:

3.3.Module创建
​ Driver Module和Library Module创建步骤相仿:

(1)选择或者创建一个Package,来存放Module

(2)为该Module创建一个目录,并且放入一个inf文件

(3)在inf文件中添加Package dependencies

(4)在inf文件中添加PPI Protocol Guid等相关dependencies

(5)如果模块依赖PPI Protocol Guid,则需要加入项[depex]

(6)创建源文件,并且在inf中填入源文件的相对路径

3.4.增加Module目录
​ Module目录需要按照以下规则添加到合适的Package中:

3.5.INF文件写法
​ INF文件是对于所在Module的说明文件,放在根目录中,其中:

(1)Module的基础信息有:名字、GUID、模块类型等

(2)Module所依赖的所有Packages路径(根目录的相对路径)

所有模块都依赖MdePkg,需要将其加入
如果使用来自Intel框架规范的定义,则IntelFrameworkPkg也是必需的
如果使用到了其它Package中的内容,则需要添加其它Packeage的dec文件

(3)源文件或者二进制文件的路径
(4)模块用到的系列接口,如Protocol、Ppi、GUID

(5)模块所需的所有pcd和Library类的列表

(6)其它内容,例如不同类型Module需要的不同的依赖部分

App Module的inf文件:

Library Module的inf文件

3.6.添加Source文件
1.INF中的 [sources] 模块定义了Source文件的相关内容,相关规则如下:

(1)不同的体系结构Source文件,放在不同的模块下,例如:

(2)使用的不同工具链需要标注,例如:

相关Tool Tag

3.7.添加Library Class References
​ Library类(Class)将相关的宏定义、结构定义和函数声明进行了抽象;而Library实例(instance)将这些内容进行了实现。

​ Library实例(Instance)根据不同Platform,和相同Platform的不同阶段,其具体实现会有所差异。

3.7.1.在模块中使用Library类的步骤:
(1)在INF文件中,给包含Library Class的package添加Dependency

(2)在INF文件中,给Library Class添加dependency

(3)在代码头文件中,包含LibraryClass

头文件的路径是相对路径,相对于package DEC文件[include]中写的路径

实例

#include <Library/OemHookStatusCodeLib.h>
1

3.8.添加PCD References
​ EDK II引入了PCD来实现宏定义的效果。例如,对于“FeatureFlag”类型的PCD,如果PCD的值为TRUE,则会启用某些特性或功能。

​ EDK II提供了以下类型的PCD:

3.8.1.使用PCD的步骤
(1)在Module的INF文件中,为PcdLib添加Dependency

(2)在Module的INF文件中,给MdePkg添加dependency

MdePkg是必需的,因为MdePkg中的“PcdLib”Library Class,提供了PCD访问函数和宏。
(3)在代码头文件中,添加==<Library/PcdLib.h>==

(4)使用PcdLib提供的接口进行PCD Value的操作

PCD操作函数:

PCD添加与使用实例:

3.9.添加Prorocol、PPI、GUID Reference
​ Protocol、PPI、Guid是UEFI中固件之间通信的接口。

3.9.1.在模块中使用Protocol、PPI、Guid
(1)在INF文件中,对应类别([Protocol]、[Ppi]或[Guid])添加需要的Protocol

(2)相应头文件必须清楚包含在Source code的头中

头文件中都是相对路径,相对于package DEC文件[include]中写的路径

3.10.为Module添加Dependency
​ Module的Dependency限定了关于驱动程序的Entry Point的条件。

​ 通过它,可以确定PEIM和DXE模块的调度顺序。

​ 一个表达式由一个或多个Protocol、PPI或GUID和操作符组成,例如“AND”, “OR”, “TRUE”, “FALSE”, “NOT” 等。
表示gEfiSampleGuid 的值和gEfiSamplePpiGuid的值进行布尔和运算

具体运算法则描述,参考Platform Initialization Specification的Dependency Expressions 章节和
Dependency Expression Grammar章节
3.11.Define Library Class
​ Library Instance总是与Library Class相关,Library Instance实现了Library Class中定义的所有接口。

因此,==Library Class名必须在Library Instance的INF文件的[definitions]==中说明。

如下所示:

UefiDriverEntryPoint 是Library Class的名字,这个名字由Library Instance所来。

DXE_DRIVER和DXE_RUNTIME_DRIVER是这个Library Instance所支持的类型。

3.12 Driver Module的额外步骤
​ PEIM或者DXE Driver需要在INF文件中的==[Defines]中标明函数Entry_Point==。不同的Module类型具有不同的Entry_Point。

示例图:

3.13 常见Library Class
​ MdePkg中提供了许多Library Class,用来基于UEFI和PI开发固件组件。

3.14 使用HII的Module
​ DXE Module可以在BDS阶段中打印或更新,browser将使用到下列资源:

3.14.1 Forms
(1)VFR Resource File

​ VFR文件用来描述form(即格式)资源。VFR文件的用法和其它Source Code用法相似。

需要将其在Module的INF文件的[Sources]部分中列出。

用法示例:

(2)打印VFR

​ 在Module编译时,vfr文件会由Vfr编译器编译为一个.vfr文件,并且作为全局数组变量插入到Module image的IFR二进制区域中。这个全局数组变量的名字是:Vfr文件名+Bin。

示例如下:

Inventory.vfr in the MdeModulePkg\\Universal\\DriverSampleDxe driver

is compiled into the global array variable InventoryBin.

(3)将VFR全局数组变量加入HII数据库

​ 使用以下代码段:

//
// Create HII driver handle, paramter DriverHandle will hold the
// returned new handle.
// HiiLibCreateHiiDriverHandle defined in UefiHiiLib library class.
//
Status = HiiLibCreateHiiDriverHandle(&DriverHandle);
//
// Prepare HII package list, parameter InventoryBin is the VFR form data
// HiiLibPreparePackageList defined in UefiHiiLib library class
//
PackageList = HiiLibPreparePackageList (2,
&mInventoryGuid,
InventoryBin,
DriverSampleStrings);
ASSERT (PackageList != NULL);
//
// Create package into HII database via EFI_HII_PROTOCOL->NewPackageList
//
Status = gHiiDatabase->NewPackageList (
gHiiDatabase,
PackageList,
DriverHandle,
&HiiHandle
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
3.14.2 Unicode Strings
​ Unicode字符串被放到.uni文件中,并在Module的INF文件的[Sources]部分列出,和C文件一样。

示例如图:

(1)打印Unicode String文件

​ uni文件中的内容会被编译工具解析和编译为一个二进制字符串包数组。这个二进制数组的名字为ModuleName+ “Strings”。

例如:MdeModulePkg\\Universal\\DriverSampleDxe 中的inventorystring.uni最终的二进制数组名将会是:

extern UINT8 DriverSampleStrings[];
1
(2)将Strings array variable 添加进 HII Database

//
// Create HII driver handle, paramter DriverHandle will hold the
// returned new handle.
// HiiLibCreateHiiDriverHandle defined in UefiHiiLib library class.
//
Status = HiiLibCreateHiiDriverHandle(&DriverHandle);
//
// Prepare HII package list, parameter DriverSampleStrings is the
// strings binary data.
// HiiLibPreparePackageList defined in UefiHiiLib library class
//
PackageList = HiiLibPreparePackageList (2,
&mFormSetGuid,
DriverSampleStrings,
VfrBin);
if (PackageList == NULL)
return EFI_OUT_OF_RESOURCES;

//
// Create package into HII database via EFI_HII_PROTOCOL->NewPackageList
//
Status = HiiDatabase->NewPackageList (
HiiDatabase,
PackageList,
DriverHandle[0],
&HiiHandle[0]);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
4. Module的编译
​ 当Module Source Code完成以后,Module的INF文件会被加入到Platform的DSC文件里,Module将被编译到预期的二进制Image中。

​ EDK2编译系统支持编译Library Image、EFI Image、OptionRom Image。

4.1 将Module INF文件加入到Packeage DSC文件
​ 为了编译一个Module,会在DSC文件的[Components]部分,指定Module的INF文件(路径是Package目录开始的相对路径,到INF文件名字为止)

​ DSC的[Defines]部分会列出所有支持的体系。[Components]部分会详细列出Module的支持体系。

示例:

4.2. 为Module选择Library Instance(library instance不需要此步)
​ 对于Driver和Application来说,每个Library Class必须选中对应的Library Instance,并且链接到二进制EFI Image处。

​ Module INF文件的==[LibraryClasses] 部分列出了所有需要的Library Class==,这些Library Class都是Library Instance所产生的。

​ 对于不同的目的(例如对性能和大小的优化),Library Instance可以有不同的实现方式。

​ 在初始开发中,通常使用没有经过优化的Library Instance来降低开发风险。完成模块的基本功能后,可以进一步调整其大小和性能。

​ 在MdePkg中,为用户选择提供了许多可用的Library Instances,详细信息可以在其INF文件或MdePkg规范中找到。

举例:

​ 在MdePkg中,对于LibraryClass:BaseMemory的Library Instance:BaseMemoryLibOptDxe的实现,采用了寄存器操作内存的方式,获得了更好的性能表现。

​ 另一个例子是,在MdePkg中,LibraryInstance:PeiIoLibCpuIo通过使用CpuIo PPI的服务来实现IO的LibraryClass,减少了代码大小。

4.3 对于PCD的配置
​ 对于使用到PCD的Module,需要提前在Package DSC文件中配置。配置完成的PCD会被应用到Module和其链接的Library Instance两处。

​ PCD会在Package的DEC文件中声明。当PCD的Value和DEC文件中的默认值一致时,这些PCD则不需要再DSC中再次赋值。

​ 在DSC中,PCD类型和值可以根据Platform要求进行配置。在DSC中,一个PCD的类型只允许是唯一的类型。如果DSC中没有对其进行指定,那么PCD的类型将和DEC中声明的类型保持一致。如果一个PCD支持多种PCD类型,那么其默认PCD类型是固定PCD类型。

4.3.1 PCD的类型

(1)Feature Flag PCD

​ 如果一个PCD被声明为PcdsFeatureFlag ,那么它只能是FeatureFlag PCD type或者BOOLEAN data type。如果Module需要使用本类PCD,则需要在INF的[FeaturePcd]中预先指定。

只有通过FeaturePcdGet接口才可以操作此种类型的PCD

示意:

(2)Fixed PCD

​ 如果一个PCD的Value在整个编译期间都是固定的,那么它的类型应设置为PcdsFixedAtBuild 。

当Module使用此类PCD时,它可以选择归属在==Module INF的[FixedPcd]或者[PCD]==中。

​ 此外,操作本类型的PCD,需要使用FixedPcdGet或者PcdGet接口。

(3)Patchable PCD

​ 如果一个PCD的Value在二进制Image中不固定(会被修改),它的类型应设置为PcdsPatchable 。在Module中使用此类PCD时,它可以分在==[FatchPcd]或者[PCD]==。

(4)Dynamic PCD

​ 如果PCD的Value在运行期间获得,那么它的类型应设置为Dynamic。如果一个Dynamic PCD是从Driver共享的PCD数据库取得Value,那么它的类型应该是PcdsDynamicDefault;如果一个Dynamic PCD和UEFI Variable相关,那么它的类型应该是PcdsDynamicHII。

​ 当在Module中使用Dynamic PCD,那么INF的 [PCD]部分需要注明。

​ 此外,操作Dynamic PCD只有通过PcdGet、PcdSet两个接口。

示例图:

4.4 Build选项修改
​ Build Option为不同的工具链提供了不同的编译选项,来编译出Image。它们在==$(WORKSPACE)/Conf/tools_def.txt file==被定义。在这个文件中,列出了不同工具链常见的编译器选项。

编译器选项(compiler option)主要被分成两大类(详细选项可见EDK II Build Specification ):
​ 编译选项(compile option)

​ 链接选项 (link option)

​ EDKII 编译系统提供了四种层次的override机制来实现Compiler Options的定制。

影响范围从大到小,依次是:Tool_def.txt,DSC(最为推荐,只修改了DSC文件),INF。

​ 这些Options可以通过指定顺序实现互相覆盖。

4.4.1 修改Tool_def.txt
​ 直接对tool_def.txt 进行修改,将会影响workspace里的所有Modules和platforms

4.4.2 修改INF文件
​ 在Module INF文件的**[BuildOptions]**中,添加额外的编译器选项(compiler option),将会对该Module生效,并且在任何DSC中都可被编译。

不同编译工具可选的编译器选项(compiler option)并不相同

Module INF示例:

4.4.3 修改DSC文件
​ 在DSC的 [BuildOptions]部分添加额外的编译器选项(compiler option),同一个DSC中描述的所有Module的编译器选项都会被影响。

DSC示例

​ 在DSC的==[Components]部分,为某个Module添加编译器选项(compiler option),则只对该Module==,且只在该DSC生效。

DSC [Components]示例

4.5 Build Module Image
​ 在DSC文件的 [Components] 中所添加的描述

4.5.1 Build Package(-p)
​ 如果 [Components] 没有指明build module option,那么package DSC的[Components]所列出来的Module都会被编译。如果build module option有多个,只有最后一个会生效

示例:

4.5.2 Build Module(-m)
​ 如果一个指定Module需要build,则其必须首先在DSC的 [Components] 中列出。如果没有列出,DSC的所有Module都会被build。如果command line指定指令不止一次,只有最后一个会生效。

示例:

4.5.3 Build Arch
​ 支持的ARCH Option有: IA32、X64、IPF、EBC。如果在命令行中指定了不止一次,则每个ARCH会按顺序构建。

示例:

4.5.4 Build Target
​ 支持的Target有:DEBUG和RELEASE。如果在命令行上指定多次,则按顺序构建每个Target。

示例:

4.5.5 Build Tool Tag Name
​ Tool Tag Name定义在Conf\\Tools_def.txt文件中,表示编译器工具链。例如,MYTOOLS是Microsoft VS2005工具链的默认Tool Tag Name。

​ 如果在命令行上指定多次,则会按顺序使用每个工具链。

4.5.6 示例:Build Helloworld

​ 在build DEBUG文件夹中会创建以下文件:EFI Image、Intermediate Files、AutoGen.h、AutoGen.c、Module.map 文件

​ EDK II build工具根据所需的Module信息,为每个Module生成AutoGen.h和AutoGen.c文件。其中包括了依赖的pcd,Guid值和包含Module Entry Point相关的函数。

​ 这些AutoGen函数在ModuleEntryPoint库实例中被引用。对于每个Module, Entry Point函数首先调用AutoGen Code,然后进入Module functions。

​ Module.Map是由compiler tool生成的,用于列出该Module中所有函数及其相对地址。它们可用于在运行时定位Module函数地址。

AutoGen.c如图:

PcdDxe.map如图:

4.6 Build EFI Option Rom Image
​ EFI Option Rom是一个标准的EFI image。EFI image可以通过上述build Module命令编译。与普通Module的不同在于: EFI Option Rom Image INF文件的 [Defines] 中会包含相关的PCI Option。当Module INF文件中设置了PCI Option。这个Module会被编译成EFI和Option Rom Images。

​ 在build DEUBG文件下,会出现ModuleName.efi 和ModuleName.rom

Option Rom INF示例:

4.6.1 常见build Module报错

4.7 Debug Module
​ 在开始Debug Module之前,需要执行以下4个步骤:

指令:“Build –b DEBUG”
​ EDK II支持生成DEBUG/RELEASE目标。target.txt中的"BuildTarget"字段与" ToolChain "字段共同生效,以确定编译器tool-chain和build option的实际路径。开发人员可以直接打开

$(WORKSPACE)\\Conf\\ TARGET .txt并更改“TARGET = DEBUG”作为调试提示。开发人员也可以使用命令行来覆盖该值,例如在调试提示中使用“build -b DEBUG”。

选择合适的DebugLib Library Instance
​ 对于DebugLib库类,MdePkg和IntelFrameworkModulePkg核心包提供了几个库实例。其中包括BaseDebugLibNull、BaseDebugLibSerialPort UefiDebugLibConOut,

UefiDebugLibStdErr, PeiDxeDebugLibReportStatusCode。开发人员可以按实际要求在Package的DSC文件中,选择合适的DebugLib库实例。

配置DebugLib使用的Pcds
​ DebugLib library class header定义了两个用于调试库配置的pcd。与debug ability相关的pcd包括PcdDebugPropertyMask和PcdDebugPrintErrorLevel。前者用于控制print/assert,并确定assert宏是通过CpuDeadLoop还是通过BreakPoint实现的。对于后者,开发人员可以设置各种值来控制打印或过滤错误信息。

Change build option
​ 开发人员可以修改或重写module build option。例如,开发人员可以使用Microsoft 编译器的“/Od”选项来禁用编译器的优化,并避免无序指令。还可以使用Microsoft编译器的“/FAsc”选项来生成一个源代码和汇编(.cod)文件来帮助调试。

4.7.1 基本调试方法
​ 以下是三个基本的调试方法:

使用DEBUG打印语句
​ 在EDK II项目中,有一组PCD,用于开启/关闭DEBUG。开发人员可以在开始调试时打开该功能。

CpuDeadLoop()
​ 开发人员可以使用API halt control flow,有助于快速定位问题。

Module的 Map file
​ EDK II为每个模块生成一个对应的FV Map文件

​ 开发人员可以根据已加载Module的基址和Map File来计算函数的内存地址。

(2)通过Variable

​ Variable是一对 key/Value,由标识信息加上属性(Key)和任意数据(Value)组成。Variables传递数据,主要为:存储平台实现的EFI环境、EFI OS Loader和在EFI环境中运行的其他应用之间的传递。

​ UEFI应用程序可以通过UEFI Runtime Services读写变量GetVariable()和SetVariable()。因为UEFI App必须在Dxe/UEFI驱动之后运行,不可缺少Variable Arch protocol。

2.UEFI Driver
​ 满足UEFI Driver Model的Driver被称为UEFI Driver。UEFI Driver初始化的过程中不允许接触任何硬件资源。相反,它会在UEFI驱动的ImageHandle上安装一个EFI_DRIVER_BINDING_PROTOCOL的实例。

​ 之后,UEFI Driver可能会被EFI_DRIVER_BINDING_PROTOCOL 调用,来对于一个指定的Hardware进行支持性测试。这项测试是为了确定一个driver是否支持一个给定的controller。

​ 该测试在不对controller引起负面影响的前提下,必须越快越好。Controller的大部分初始化都是在EFI_DRIVER_BINDING_PROTOCOL 服务的开始和结束时完成的。

UEFI Driver和DXE Driver的区别见第六节
2.1 UEFI Driver介绍
2.1.1 INF文件
​ UEFI Driver的INF file里,[Defines] 部分的Module TYPE必须指定为UEFI Driver。

​ UEFI Driver没有 [Depex] 的部分。因为它一直依赖于DXE架构下的protocols。为了实现这一点,UEFI Driver Entry Point库实例将所有DXE架构下的protocols的依赖关系附加到了Module Image的 [Depex] 部分。

示例:

2.1.2 UEFI Driver Entry Point
​ 下表列出了UEFI驱动程序入口点中最常用的Entry Point。

如图:

(1)示例:

2.1.3 Get Service Table
​ UEFI Driver可以使用UEFI Boot Services、UEFI Runtime Services和UEFI System Tables。为此,EDK II 提供了UefiBootServicesTableLib 和 UefiRuntimeServicesTableLib库,以方便开发者访问。

Global Variables

2.1.4 UEFI Drivers之间的通信
(1)通过Protocol

​ UEFI驱动可以使用Protocol访问其他Module产生的Protocol接口

(2)通过Variable

​ UEFI驱动可以通过UEFI Runtime Services读取和写入变量GetVariable()和SetVariable()。

UEFI Driver和DXE Driver的区别
​ UEFI driver 和DXE 的driver的主要区别是:是否遵循UEFI Driver Module

​ 1.UEFI Driver不需要依赖Dependency来决定执行的顺序

​ 2.UEFI Driver必须可以被重复执行

​ 3.UEFI Driver不需要即时启动

​ 4.UEFI Driver支持硬件的热插拔(Hot-Plug)

​ 5.UEFI Driver支持软件的热插拔(Unload)

​ 6.UEFI Driver所有的Function都是Device(Handle)结合Driver(Protocol)

3.SEC Module
​ SEC模块是上电后执行的第一个模块。它负责配置PEI环境的内存调用堆栈。此外,该模块发现并将控制权传递给PEI Core,将信息传递给PEI Foundation。

3.1.1 INF File

(1)对于一个物理平台来说,Module_Type必须是SEC,对于一个模拟平台来说,可以是SEC,也可以是USER_DEFINED

(2)对于IA32来说,入口一定是**_ModuleEntryPoint**

(3)对于安腾处理器家族平台,入口点是可配置的,例如SAMPLE_ENTRY。但是,这个入口点==应该添加到[BuildOptions]==部分,如下所示:

4.Pre-EFI模块
​ Pre- EFI模块提供了一个基于标准的平台初始化。PEI阶段的责任是初始化足够的系统,来为接下来的阶段提供一个稳定的基础。

​ 强烈推荐PEI模块只做最小要求的工作来满足后续阶段的要求。PEI Foundation建立了所有PEI Module使用的PEI Service Table。

​ PEI阶段允许在Memory可用之前,执行C语言代码实现的PEI Module。这是通过配置CPU的资源,例如CPU data cache来实现内存栈。

4.1 PEIM INF 文件
​ PEI Module文件示例:

4.2 PEI Module的EntryPoint
​ 上述示例中,Entry Point是PeimSampleInitialize ,下面是Entry Point的原型。

FileHandle是正被调用文件的handle,PeiServices是PEI Services Table的间接指针

示例图:

4.3 获取PEI Services
​ EDKII 在PEI Services Library Class中提供了所有的PEI Services的API。开发人员可以使用PEI Services Library来调用PEI Services。

​ EDKII 为PEI Modules提供了PEI Services Table Library,来获取PEI Service Table。除了从PEI Module的Entry Point中的输入函数来获取PEI Services Table指针。EDKII 还允许使用定义在PEI Services Table Pointer Library中的GetPeiServicesTablePointer() 来获取PEI Services Table指针。

4.4 PEIM Modules之间的通信
​ PEIMs之间有三种通信方式:PPIs、hob和动态pcd。

4.4.1 PPI
​ PEIM模块可以使用一种称为PEIM-toPEIM Interface (PPI) 的结构来互相通信。每个PPI有一个GUID。PEI Service Table 提供了一些PEI Services来使用PPI的数据库。

​ 在EDKII 中,一个PEIM Module可以通过GUID调用 PeiServicesInstallPpi() ,来Publish自己的PPI Services到PPI database中。另一个PEIM Module可以根据GUID来调用PeiServicesLocatePpi() ,在PPI database中定位PPI Services。

(1)Installing a PPI

​ 如果一个Module A想要publish一个PPI Services的Template(假设其中包括三个API:interface1、2、3). 他可以通过使用PeiserciecesInstallPpi来Install 这个PPI Template。

示例:

(2)Locating a PPI

​ 如果Module B需要调用PPI Template提供的Interface2(),他可以通过使用下列代码来Locate。

示例:

4.4.2 HOB
​ PEIM Modules可以build一个Hand Off Block(HOB)来传递一些信息给DXE Module和DXE Foundation。此外,其他的PEIMS可以通过使用PEI Service Table中的HOB Services来从HOB中获得相似的信息。

​ 在EDKII 中,Hob Library为PEIMs和DXE Driver提供了通用的接口来使用HOBs。

4.4.3 PCD
​ PEIM可以通过Dynamic PCD来和其他PEIMs通信。和HOBs一样,只有PEIMs可以获取

动态PCD的值,这些值之前由DXE Driver设定。Get PCD的用法在附录A Dynamic PCD有介绍。

4.5 与DXE Module的通信
4.5.1 HOB
​ 通过使用Hand-Off Block, PEIMs可以将一些信息传递给DXE Foundation和DXE模块,例如在PEI阶段发现的内存信息。

​ 在EDK II中,Hob Library提供了一组接口来帮助构建Hob,例如BuildGuidHob ()。Hob库还为PEIMs和DXE Drivers提供了一组api来定位Hob。

例如:

4.5.2 Variable
​ PEIMs可以读取以前由DXE驱动程序分配的Variable。PEIMs不能写入Variable。

PEIMs可以使用ReadOnlyVariable2 来获取Variables。

具体步骤如下:
(1)定位ReadOnlyVariable2 PPI

(2)当size为0时,调用GetVariable()来获取Variable的实际大小。

(3)为Variable分配内存空间

(4)再一次以实际大小调用GetVariable()

代码示例:

4.5.3 PCD
​ PEIMs可以通过动态pcd与DXE驱动程序进行通信。PEIMs可以获得以前由DXE驱动程序设置的动态pcd值。获取PCD的用法见附录A。

4.6 Boot Mode
​ 有时候,PEIMs需要确认boot mode(S3、S5等等),并且根据boot mode采取合适的举动。例如VariablePei Module在recovery boot path下时,将不会Install EFI ReadOnlyVariable2Ppi

​ PEI Service Table提供了一系列的services来设置或者获取boot mode。PEI Service Library中相应的API为:SetBootMode() and GetBootMode() 。

get boot mode的示例:

4.7 PEIMs Excution in Place(XIP)
​ 大多数PEIMs都是XIP(Excution in Place:就地执行),并且不被压缩。因为他们在permanent memory之前运行。

​ 在代码的空间复杂性和Module的时间复杂性之间有这样的权衡:保持Modules的小还是保持代码路径的短。

​ PEIM 代码的数量和复杂性需要简化。例如:对于在Flash上运行的代码来说,需要避免很大的循环。

​ 当PEIM尝试将自己load到system memory并且再次运行时,它可以使用RegisterForShadow () 来实现。RegisterForShadow () 在Pei Service Table中。

4.8 PEIMs的Dependency
​ PEIM必须有Dependency的部分。PEIM在Dependency的条件全部满足以后被dispatch。

​ 如果一个PEIM的Dependency是True,那么其可以立刻被dispatch。在扩展INF文件中,Dependency部分包含在[Depex]部分中。PPI dependency被PPI GUID定义。

示例:
​ 该模块只在Read Only Variable2 Ppi, CachePpi and CapsulePpi全部Install完成后dispatch。

5.DXE Drivers(非UEFI Drivers)
​ DXE驱动指的是满足PI Spec的驱动。PI Spec将DXE驱动分成两类:UEFI驱动模型的驱动和非UEFI驱动(普通DXE Driver)模型的驱动。本节重点是普通DXE Driver。

​ 非UEFI驱动模型的驱动在DXE阶段早期执行。这些驱动是DXE Foundation产生所有要求服务的先决条件。

​ DXE驱动程序必须设计成不需要不可用的服务。考虑到这一限制,所有可能的工作都应该交由UEFI驱动程序完成。

UEFI Driver和DXE Driver的区别见第六节
5.1 INF 文件
​ DXE驱动程序需要扩展INF文件。INF文件的基本介绍请参见第二章。

​ DXE驱动的 [Defines] 部分应该按照如下修改:

5.2 DXE Driver Entry Point
​ UEFI驱动程序入口点只允许将protocol实例安装到自己的Image Handle上,不能接触任何硬件。与UEFI驱动程序入口点不同,DXE驱动程序入口点没有这样的限制。它可以将任何protocol安装到system中,并且操控必要的硬件进行软件初始化。

​ 在下面的例子中(来自MdeModulePkg中的WatchDogTimerDxe驱动程序),如果protocol尚未安装,DXE驱动程序入口点将安装它的Architectural Protocol。

函数示例:
​ DXE驱动程序入口点的两个参数是ImageHandle和SystemTable。

5.3获取Services Table
​ DXE Drivers的Services Table可能会涉及:UEFI Boot Services, UEFI Runtime Services, and DXE Services。此外,DXE Driver还可以参考UEFI System Table。

​ UEFI Boot Services, UEFI Runtime Services, and UEFI System Table在UEFI Spec中都有定义。DXE Services在PI中有定义。

​ DXE Driver可以通过下列Library Class提供的全局变量,检索这些tables。

5.4 DXE Drivers之间的通信
​ DXE Drivers之间的通信方式主要包括:protocol、variable和PCD

5.4.1 Protocol
​ UEFI Spec定义了一系列的boot services来handle protocols,包括:**install protocol的services **和 检索protocols的services。

如图:

​ 首先,要使用这些protocols,Module开发人员必须在INF文件中声明Module使用的protocols,然后写代码来使用这些protocols。

(1)下面的例子演示了DXE Driver如何产生一个protocol

(2)下面演示DXE Driver如何retrieve一个protocol并且调用这个API

5.4.2 Variables
​ Variables被定义为 一对key/ Value ,这对键值对由key(确认信息加上属性)和 value(任意数据)组成。Variables是为了存放数据而使用,这些数据是在平台安装的EFI环境和EFI环境运行的EFI OS Loader和其他App之间传递的数据。

​ DXE Driver可以通过UEFI Runtime Services提供的GetVariable() and SetVariable() 来读写Variable。这两个Services在DXE刚开始的时候并不可用。

​ 需要对易失环境Variables进行只读或者读写的DXE Drivers,必须在INF的dependency中中加入EFI_VARIABLE_ARCH_PROTOCOL 。

​ 需要对非易失环境Variable进行写操作的DXE Driver,必须在INF的dependency中中加入EFI_VARIABLE_WRITE_ARCH_PROTOCOL 。

​ 环境Variable 服务的完整实现在EFI_VARIABLE_ARCH_PROTOCOL and EFI_VARIABLE_WRITE_ARCH_PROTOCOL 安装之前不可用。

对Variable读写的Sample Code示例:

5.4.3 动态PCD
​ EDK II提供动态pcd作为模块间通信的高级机制。详见附录A。

5.5 DXE Driver和PEIMs的通信
​ DXE驱动程序与PEIM之间的通信通道,包括HOB、variable、PCD。

5.5.1 HOB
​ HOB是将数据从PEI传递到DXE的单向通道。HOB列表是在PEI阶段提供的,在DXE阶段必须将其视为只读数据结构。它传递DXE Foundation启动时系统的状态。DXE驱动程序不能修改HOB列表的内容。

​ HobLib提供了一组api来构建和解析HOB列表。由于DXE驱动程序只读取HOB列表,所以DXE驱动程序的模块编写者可以专注于解析HOB列表的api。

下面的例子展示了几种典型的使用类型:
(1)遍历HOB列表中的所有HOB

(2)仅检索HOB列表中特定类型的第一个HOB(以CPU HOB为例)

(3)遍历HOB列表中的特定类型HOB(以CPU HOB为例)

(4)仅检索HOB列表中具有特定GUID的第一个GUIDed HOB

(5)在HOB列表中使用特定的GUID遍历GUIDed HOB

5.5.2 Variable
​ 非易失性变量可以作为从DXE向PEI传递数据的通道。因为只有DXE驱动程序可以写入变量,而PEIM只能读取变量,所以这个从DXE到PEI的通道也是一个单向通道。

5.5.3 动态PCD
​ 非易失性动态PCD也是DXE驱动程序和PEIM之间通信的高级机制。请参考附录A。

5.6 Dependency 表达式
​ Dependency Expression指定DXE驱动程序需要执行的protocol。EDK II中,在INF文件的[Depex]部分指定。

示例(只有在安装了列出的所有四种协议之后才能执行此驱动程序):

5.7 EVT_SIGNAL_EXIT_BOOT_SERVICES 的handle
​ 当操作系统即将完全控制平台时,一些DXE驱动程序需要将它们的控制器置于静止状态或执行其他控制器特定的操作。

​ 在这种情况下,DXE驱动程序应该创建一个信号类型事件,当EFI OS Loader调用gBS->ExitBootServices()时通知该事件。

​ 此Event的通知功能不允许使用内存分配services,或者调用任何使用内存分配services的函数,并且应该只调用已知没有使用内存分配的函数services,因为这些services修改当前内存映射。

通知功能和事件注册模板代码如下:

5.8 DXE Runtime Driver
​ DXE Runtime Driver可以运行在boot services和runtime services环境下。这意味着这些Modules产生的services在ExitBootServices() 调用前和调用后皆可用,包括OS运行的时候。如果SetVirtualAddressMap() 被调用,那么根据OS提供的虚拟地址映射,这种类型的Modules会被重新定位。

​ DXE Foundation被认为是一个引导服务组件,所以当ExitBootServices() 被调用时,DXE Foundation也可以被发布。因此,runtime时的驱动程序可能不会使用任何UEFI Boot Services, DXE Services ,或者调用ExitBootServices()后引导服务驱动程序产生的服务。

​ DXE runtime driver在INF文件中将MODULE_TYPE定义DXE_RUNTIME_DRIVER。此外,因为DXE Runtime Driver在其生存周期中导致了

SetVirtualAddressMap() 。它可能需要为event EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE注册一个event handle。

5.8.1 INF文件
​ 对于DXE Runtime Driver,Module type应该是DXE Runtime Driver。

示例:

5.8.2 Virtual Address Event的Handle
​ 当OS调用SetVirtualAddressMap() ,可能需要通知DXE Runtime Driver。在此情况下,DXE Runtime Driver必须创建一个signal类型的Event,当SetVirtualAddressMap() 被OS调用的时候,该Event将被通知。调用SetVirtualAddressMap() 允许DXE Runtime Driver讲指针从物理地址翻转为虚拟地址。

​ DXE Runtime Driver创建的signal类型的Event所用到的通知函数不允许直接或者间接使用UEFI Boot Services、**UEFI Console Services **或者 UEFI Protocol Services 。因为当调用SetVirtualAddressMap()之时,这些Services都将不可用。

​ 通常,DXE Runtime Driver创建的signal类型的Event所用到的通知函数使用

ConvertPointer() 来将指针从物理转换为虚拟。

通知函数和Event注册模板代码如下:

5.9 DXE SMM Driver
​ DXE SMM Driver这种类型由load到SMRAM中的SMM Driver使用。因此这种类型只有IA32和X64 CPU可用。这些Module由SMM Foundation分配,并且永远不会损坏。这意味着这些Module产生的services在ExitBootServices(). 之后依然可用。 SMM Driver的生命周期可以被分为两个阶段:SMM初始化和SMM Runtime,两个阶段有不同的限制条件。

​ SMM初始化是SMM驱动程序初始化的阶段,它从调用直到驱动程序的入口点开始,并从驱动器的入口点返回。

​ SMM Runtime是SMM Driver初始化的阶段。这个阶段在驱动入口点返回以后。

5.9.1 INF 文件
​ 对于SMM驱动程序,MODULE_TYPE为DXE_SMM_DRIVER。

示意图:

5.9.2 限制条件
​ SMM驱动程序模型有类似于DXE运行时驱动程序的约束。

​ 在SMM Runtime运行期间,drivers可能不可以使用内核protocol services。有相关的SMST based services供Drivers使用。但是UEFI System Table和其他在boot services阶段之间安装的protocols可能并不一定可用。

​ 在SMM 初始化期间,UEFI Boot Services、UEFI Runtime Services、SMST-based services都是可用的。

5.9.3 SMM Driver初始化
​ 当Driver加载到SMRAM并且Driver的Entry Point被调用时,SMM Driver的初始化阶段就开始了。SMM Driver的初始化阶段终止于其Entry Point的return。简单来说,整个SMM Driver初始化的过程就是SMM Driver Entry Point运行的整个过程。

​ 在SMM Driver初始化的过程中,SMM Driver可以使用两类protocol:UEFI protocol和SMM protocol。

​ UEFI Protocol指的是Install和discover时使用UEFI Boot Services的protocols。SMM Driver只有在初始化的过程中,才可以locate和使用UEFI Protocols。

​ SMM Protocols指的是使用System Management Services Table(SMST)来install和discover的protocols。

​ 在SMM Drivers初始化期间,SMM Drivers不允许使用UEFI Boot Services Exit() and ExitBootServices()。

5.9.4 SMM Driver Runtime
​ SMM Driver Runtime期间,SMM Driver只允许使用SMST-based Services。此外,对于不同的平台体系而言,SMM Driver可能没有权限使用SMRAM以外的内存区域。同样,UEFI Drivers可能不允许使用SMRAM里的内存区域。

​ 这些SMM Driver Runtime的特性,导致了关于UEFI Services用法的一些限制。

(1)在SMM Driver初始化期间locate的Interface和services,在SMM Driver Runtime期间不可以被调用或者引用。

(2)SMM Driver 初始化期间创建的 Events必须在 Driver Entry Point退出之前关闭。

6.UEFI Driver和DXE Driver的区别和联系
​ UEFI Driver和DXE Driver的主要区别在于:是否满足UEFI Driver Model。

​ DXE Driver是在编写驱动的时候主动寻找设备并且对其进行初始化;UEFI Driver则是系统服务自己根据设备寻找合适的驱动,然后对其进行初始化。前者一般在驱动运行的时候就直接完成。后者需要先对驱动进行注册,然后通过调用系统服务来完成初始化。

两种类型的示意图:

6.1 两种Driver的Entry Point
​ DXE Driver和UEFI Driver的代码结构类似,主要区别在于:Driver的Entry Point做了什么。

6.1.1 DXE Driver
​ 在一个DXE Driver的INF文件中,Entry Point是设备初始化的函数。

[Defines]
INF_VERSION = 0x00010005
BASE_NAME = DxeDriverInBds
FILE_GUID = 04687443-0174-498F-A2F9-08F3A5363F84
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = DxeDriverEntry
1
2
3
4
5
6
7
6.1.2 UEFI Driver
​ UEFI Driver只是安装了一个Protocol。以SnpDxe这个Module为例,gSimpleNetworkDriverBinding这个Protocol就是这个设备在DXE阶段安装的Protocol。所有符合UEFI Driver Model的驱动都会安装一个类似的Protocol。

​ 简而言之,UEFI Driver就是在DXE阶段安装了这样的Protocol,然后在 gBS -> Connect Controller的时候。首先将会执行 xxSupported()函数,根据 EFI SUCCESS返回值继续执行 xxStart()函数,该 xxStart()函数中就实现了设备初始化的代码。

SnpDXE.inf的 xxSupported()函数和主要流程如下:

当扫描的这个设备的时候(设备用Controller表示),先判断它是否已经Install了DevicePathProtocol,没有就表示这个设备还没有准备好(或者说不是设备),后面的xxxStart()不用执行;

然后判断NetworkInterfaceIdentifierProtocol是否安装,这个是网卡驱动一定会装的Protocol,Snp驱动底层的操作需要依赖于它,所以一定要安装,如果没有就不会执行后面的操作;

判断NetworkInterfaceIdentifierProtocol是否满足要求,如果不满足则不会执行xxxStart()函数。

​ 如果以上条件都满足,就可以认为该设备是一个网卡,然后这个驱动就会被执行(执行xxxStart()函数),而之前获取到的DevicePathProtocol和NetworkInterfaceIdentifierProtocol就会成为操作正确设备的基础。

EFI_STATUS
EFIAPI
SimpleNetworkDriverSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)

EFI_STATUS Status;
EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NiiProtocol;
PXE_UNDI *Pxe;

Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
NULL,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (EFI_ERROR (Status))
return Status;

Status = gBS->OpenProtocol (
Controller,
&gEfiNetworkInterfaceIdentifierProtocolGuid_31,
(VOID **) &NiiProtocol,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);

if (EFI_ERROR (Status))
if (Status == EFI_ALREADY_STARTED)
DEBUG ((EFI_D_INFO, “Support(): Already Started. on handle %p\\n”, Controller));

return Status;

DEBUG ((EFI_D_INFO, “Support(): UNDI3.1 found on handle %p\\n”, Controller));

//
// check the version, we don’t want to connect to the undi16
//
if (NiiProtocol->Type != EfiNetworkInterfaceUndi)
Status = EFI_UNSUPPORTED;
goto Done;

//
// Check to see if !PXE structure is valid. Paragraph alignment of !PXE structure is required.
//
if ((NiiProtocol->Id & 0x0F) != 0)
DEBUG ((EFI_D_NET, “\\n!PXE structure is not paragraph aligned.\\n”));
Status = EFI_UNSUPPORTED;
goto Done;

Pxe = (PXE_UNDI *) (UINTN) (NiiProtocol->Id);

//
// Verify !PXE revisions.
//
if (Pxe->hw.Signature != PXE_ROMID_SIGNATURE)
DEBUG ((EFI_D_NET, “\\n!PXE signature is not valid.\\n”));
Status = EFI_UNSUPPORTED;
goto Done;

if (Pxe->hw.Rev < PXE_ROMID_REV)
DEBUG ((EFI_D_NET, “\\n!PXE.Rev is not supported.\\n”));
Status = EFI_UNSUPPORTED;
goto Done;

if (Pxe->hw.MajorVer < PXE_ROMID_MAJORVER)

DEBUG ((EFI_D_NET, "\\n!PXE.MajorVer is not supported.\\n"));
Status = EFI_UNSUPPORTED;
goto Done;

else if (Pxe->hw.MajorVer == PXE_ROMID_MAJORVER && Pxe->hw.MinorVer < PXE_ROMID_MINORVER)
DEBUG ((EFI_D_NET, “\\n!PXE.MinorVer is not supported.”));
Status = EFI_UNSUPPORTED;
goto Done;

//
// Do S/W UNDI specific checks.
//
if ((Pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) == 0)
if (Pxe->sw.EntryPoint < Pxe->sw.Len)
DEBUG ((EFI_D_NET, “\\n!PXE S/W entry point is not valid.”));
Status = EFI_UNSUPPORTED;
goto Done;

if (Pxe->sw.BusCnt == 0) 
  DEBUG ((EFI_D_NET, "\\n!PXE.BusCnt is zero."));
  Status = EFI_UNSUPPORTED;
  goto Done;

Status = EFI_SUCCESS;
DEBUG ((EFI_D_INFO, “Support(): supported on %p\\n”, Controller));

Done:
gBS->CloseProtocol (
Controller,
&gEfiNetworkInterfaceIdentifierProtocolGuid_31,
This->DriverBindingHandle,
Controller
);

return Status;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
6.2 两种驱动的开始处
6.2.1 DXE Driver
​ 驱动被调用的基本函数是LoadImage()和StartImage(),这两个函数都是Boot Service,所以可以在DXE和BDS阶段的大部分地方调用。

​ DxeMain.c里面的DxeMain()函数,其中的CoreDispatcher()就是用来执行各个驱动的,除非自己写代码,否则DXE驱动都会在这个位置执行。

6.2.2 UEFI Driver
​ 之前已经提到,ConnectController()函数里面会执行驱动的xxxSupported()函数,对应调用位置如下:

do

//
// Loop through the sorted Driver Binding Protocol Instances in order, and see if
// any of the Driver Binding Protocols support the controller specified by
// ControllerHandle.
//
DriverBinding = NULL;
DriverFound = FALSE;
for (Index = 0; (Index < NumberOfSortedDriverBindingProtocols) && !DriverFound; Index++) 
  if (SortedDriverBindingProtocols[Index] != NULL) 
    DriverBinding = SortedDriverBindingProtocols[Index];
    PERF_START (DriverBinding->DriverBindingHandle, "DB:Support:", NULL, 0);
    Status = DriverBinding->Supported(
                              DriverBinding,
                              ControllerHandle,
                              RemainingDevicePath
                              );
    PERF_END (DriverBinding->DriverBindingHandle, "DB:Support:", NULL, 0);
    if (!EFI_ERROR (Status)) 
      SortedDriverBindingProtocols[Index] = NULL;
      DriverFound = TRUE;

      //
      // A driver was found that supports ControllerHandle, so attempt to start the driver
      // on ControllerHandle.
      //
      PERF_START (DriverBinding->DriverBindingHandle, "DB:Start:", NULL, 0);
      Status = DriverBinding->Start (
                                DriverBinding,
                                ControllerHandle,
                                RemainingDevicePath
                                );
      PERF_END (DriverBinding->DriverBindingHandle, "DB:Start:", NULL, 0);

      if (!EFI_ERROR (Status)) 
        //
        // The driver was successfully started on ControllerHandle, so set a flag
        //
        OneStarted = TRUE;
      
    
  

while (DriverFound);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
四、附录
1.Dynamic PCD
​ Dynamic类型的PCD用来动态配置或者设置值。相比之下,Static类型的PCD如FeatureFlag, FixedPcd, PatchablePcd等等在build时已经在最后生成的FD image中被固定了。

​ Dynamic 决定意味着下列三种情况中的一种。

(1)PCD 设定值由Driver在执行过程中产生和使用。

(2)PCD 设定值可由用户在setup中配置

(3)PCD 设定值由平台OEM供应商在指定区域中产生。

2.什么时候使用Dynamic PCD
​ Module开发人员在写Source Code或者INF时,并不关心PCD是Dynamic还是Static。Dynamic PCD和Dynamic类型由平台整合商在平台DSC文件中指明。

3.Dynamic Type的类型
​ 根据Module分发的方式,Dynamic PCD可以被分为以下几种类型:

3.1 Dynamic
​ 如果Module在Source Code中发布,并且将以平台DSC编译,那么这个Module使用的Dynamic PCD可以用这样的方式访问:PcdGetxx(PcdSampleDynamicPcd)

​ 在编译平台,编译工具将PcdSampleDynamicPcd翻译为参数 Token Space Guid:Token Number。

3.2 Dynamic Ex
​ 如果一个Module以binary形式release,并且没有包含在平台编译中,那么这个Module使用的Dynamic PCD必须以这样的方式访问:PcdGetxxEx(gEfiMyTokenspaceGuid, PcdSampleDynamicPcd)

3.3 Default Storage
​ PCD值保存在PCD数据库中,PCD数据库由PCD Driver在boot time memory中维护。Default Storage类型用来在PEIM和DXE Drivers或者DXE和DXE Drivers中通信。所有的Set或者Get的值在boot time memory关闭后将会丢失。

​ [PcdsDynamicDefault]在平台DSC文件中,被作为此类型的PCD的名字。[PcdsDynamicExDefault]用于dynamicEx类型的pcd。

3.4 Variable Storage
​ 此种PCD值保存在Variable区域。作为默认的存储类型,这种类型的PCD可以被PEI DXE的驱动通信使用。除此以外,这种类型的PCD也可以被用来保存通过Variable Interface的HII所设置的相关的值。

​ 在PEI阶段,这个PCD值可以被获得但是不可以被设置,因为这个Variable区域是只读的。

​ [PcdsDynamicHii]被用作平台DSC文件中这种类型的PCD的节名。

​ [pcdsdynamicexhibit]表示PCD的dynamicEx类型。

以上是关于udk开发-稀里糊涂的主要内容,如果未能解决你的问题,请参考以下文章

三年一个人使用虚幻引擎(UDK)开发的一个游戏心路

UDK2018 来了----常见编译问题

UDK2018 来了----常见编译问题

Windows7下UEFI开发EDK2环境搭建(VS2013+UDK2015+IASL+patch+Openssl)

Windows7下UEFI开发EDK2环境搭建(VS2013+UDK2015+IASL+patch+Openssl)

Windows7下UEFI开发EDK2环境搭建(VS2013+UDK2015+IASL+patch+Openssl)