1、库模块
(1)开发大型工程的时候经常会用到库,设置 MODULE_TYPE 为 BASE ;设置 LIBRARY_CLASS 为library 的名字。同时,不要设置 ENTRY_POINT。 [Packages] 块列出库引用到的包, [LibraryClasses] 列出包所依赖的其他库。
(2)有些库仅能被某些特定的模块调用,编写这种库时需在工程文件中声明库的适用范围,声明方法是在 [Def ines] 块的 LIBRARY_CLASS 变量中定义,格式如下:
LIBRARY_CLASS = 库名字 | 适用模块类型 1 适用模块类型 2
(3)编写了库之后,要使库能被其他模块调用,还要在包的 .dsc 文件中声明该库
(4)如果库使用之前需要进行初始化,在库的工程文件需指定 CONSTRUCTOR 和 DESTRUCTOR,
CONSTRUCTOR 函数会加入到 ProcessLibraryConstructorList 中,这个 CONSTRUCTOR 函数会
在 ENTRY_POINT 之前执行; DESTRUCTOR 函数会加入到 ProcessLibraryDestructorList 中,
这个 DESTRUCTOR 就会在 ENTRY_POINT 之后执行。
例如,如果 zlib 库在被调用之前需在 InitializeLib() 中初始化,程序结束之前需调用
LibDestructor() 清理 zlib 库占用的资源,那么要在工程文件中做如下设置:
[Def ines]
…
CONSTRUCTOR =InitializeLib
DESTRUCTOR =LibDestructor
然后还需在库的源文件中提供这两个函数,如示例 3-15 所示。
【示例 3-15】 zlib 库的构造函数和析构函数。
RETURN_STATUS EFI_API InitializeLib()
{
EFI_STATUS Status;
…// 初始化库
return Status;
}
RETURN_STATUS EFI_API LibDestructor ()
{
EFI_STATUS Status;
…// 清理库所占资源
return Status;
}
2、UEFI 驱动模块
在 UEFI 中,驱动分为两类:一类是符合 UEFI 驱动模型的驱动,模块类型为 UEFI_DRIVER,
称为 UEFI 驱动;另一类是不遵循 UEFI 驱动模型的驱动,模块类型包括 DXE_DRIVER、
DXE_SAL_DRIVER、 DXE_SMM_DRIVER、 DXE_RUNTIME_DRIVER,称为 DXE 驱动。
在 [Sources] 块,通常含有 ComponentName.c,在此文件中定义了驱动的名字,驱动
安装后,这个名字将显示给用户。
在 [LibraryClasses] 块,必须包含 Uef iDriverEntryPoint
[Defines] 部分定义了模块类型( MODULE_TYPE )、模块的名字( BASE_NAME)、版本号( VERSION_STRING)、入口函数( ENTRY_POINT )等。
[Sources] 部分定义了本模块包含的源文件或目标文件。
[Packages] 指明了要引用的包,包中的头文件可以在库的源文件中引用。
[LibraryClasses] 列出了需要链接的库。
[Protocols] 里列出了本模块用到的 Protocol。
[BuildOptions] 列出了编译本模块中的源文件时用到的编译选项。
3、包及 .dsc、 .dec、 .fdf 文件
前面我们介绍了 .inf 文件,如果说 .inf 文件相当于 Visual studio 中的工程文件, .dsc
( Platform Description File)则相当于 Visual studio 中的 solution 文件。每个包包含一个 .dec
( Package Declaration File)文件、一个 .dsc 文件。如过一个包用于生成固件 Image 或 Option
Rom Image,这个包还要包含 .fdf( Flash Description F iles), .fdf 用于生成固件 Image、 Option
Rom Image 或可启动 Image。
build 命令用于编译包,它需要一个 .dsc 文件、一个 .dec 文件以及一个或多个 .inf
文件。
GenFW 命令用于制作固件或 Option Rom Image,它需要一个 .dec 文件和一个 .fdf
文件。
4、[Components] 块
在该区块内定义的模块都会被 build 工具编译并生成 .ef i 文件,格式如下:
[Components.$(Arch)]
Path\Exectuables.inf
或者
[Components.$(Arch)]
Path\Exectuables.inf{
<LibraryClasses> # 嵌套块
LibraryName|Path/LibraryName.inf
<BuildOptions> # 嵌套块
# 子块中还可以包含 <Pcds*>
}
如果 Path 是相对路径,则相对路径起始于 $(WORKSPACE), $(WORKSPACE) 通常是
EDK2 的根目录。在 Path 中可以使用通过 DEFINE 命令定义的宏。例如:
[Components]
DEFINE MYSOURCE_PATH = D:/Source
$(MYSOURCE_PATH)/Hello.inf # 相当于 D:/Source/Hello.inf
5、[PCD] 块 用 于 定 义 平 台 配 置 数 据。 其 目 的 是 在 不 改
动 .inf 文件和源文件的情况下完成对平台的配置。例如在 UEFI 模拟器 Nt32Pkg 的 .dsc 文件
Nt32Pkg.dsc 中,可以通过 PCD 的 PcdWinNtFileSystem 配置模拟器文件系统路径,如下
所示:
gEf iNt32PkgTokenSpaceGuid.PcdWinNtFileSystem|L".!..\..\..\..\EdkShellBinPkg\
Bin\Ia32\Apps"|VOID*|106
该项配置被竖线“ |”分为 4 个部分。第一部分中 gEf iNt32PkgTokenSpaceGuid 是名字空
间, PcdWinNtFileSystem 是变量名。第二部分是值。第三部分是变量类型。第四部分是变量
数据的最大长度。
在源文件中可以通过 LibPcdGetPtr(_PCD_TOKEN_PcdWinNtFileSystem) 获得 gEf iNt32Pkg
TokenSpaceGuid.PcdWinNtFileSystem 定义的值。
6、.dec 文件
.dec 文件定义了公开的数据和接口,供其他模块使用。它包含了必需区块 [Def ines] 以及
可选区块 [Includes]、 [LibraryClasses]、 [Guids]、 [Protocols]、 [Ppis] 和 [PCD] 几个部分。
(1. [Def ines] 块
[Def ines] 区块用于提供 package 的名称、 GUID、版本号等信息,格式如下:
[Defines]
Name = Value
Name 可以是下面 4 个: DEC_SPECIFICATION、 PACKAGE_NAME、 PACKAGE_GUID、
PACKAGE_VERSION。
例如, MdePkg.dsc 中的 [Def ines] 部分如下所示:
[Def ines]
DEC_SPECIFICATION = 0x00010005
PACKAGE_NAME = MdePkg
PACKAGE_GUID = 1E73767F-8F52-4603-AEB4-F29B510B6766
PACKAGE_VERSION = 1.03
(2. [Includes] 块
[Includes] 中列出了本 Package 提供的头文件所在的目录,格式如下:
[Includes.$(Arch)]
Path
Path 只能是相对路径,该相对路径起始于本 Package 的 .dsc 所在的目录。
例如, MdePkg.dec 文件中的 [Includes] 部分如下所示:
[Includes]
Include
[Includes.IA32] Include/Ia32 [Includes.X64] Include/X64 | # 编译 32 位程序时的头文件路径 |
# 编译 x86_64 位程序时的头文件路径 |
(3. [LibraryClasses] 块
Package 可以通过 .dec 文件对外提供库,每个库都必须有一个头文件,放在 Include\
Library 目录下。本区块用于明确库和头文件的对应关系。其格式如下:
[LibraryClasses.$(Arch)]
LibraryName | Path/LibraryHeader.h
例如,下面的代码是 MdePkg.dec 中的 [LibraryClasses] 的一部分。
[LibraryClasses]
Uef iUsbLib|Include/Library/Uef iUsbLib.h
…
[LibraryClasses.IA32, LibraryClasses.X64]
SmmLib|Include/Library/SmmLib.h
(4. [Guids] 块
在 Package\Include\Guid 目录中有很多文件,每个文件内定义了一个或几个 GUID,例如
在 MdePkg\Include\Gpt.h 文件中定义了 Gpt 分区相关的 GUID,如下所示:
extern EFI_GUID gEf iPartTypeUnusedGuid;
extern EFI_GUID gEf iPartTypeSystemPartGuid;
extern EFI_GUID gEf iPartTypeLegacyMbrGuid
可以看出这些定义仅仅是声明。那么真正的常量定义在什么地方呢?真正的常量定义在
AutoGen.c 中,其值定义在 .dec 文件的 [Guids] 区块。其格式为:
[Guids.$(Arch)]
GUIDName = GUID
回到前面讲的 Gpt 相关的 GUID,在 MdePkg.dec 中,这些 GUID 定义如下:
[Guids]
…
## Include/Guid/Gpt.h
gEf iPartTypeLegacyMbrGuid = { 0x024DEE41, 0x33E7, 0x11D3, { 0x9D, 0x69,
0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F }}
gEf iPartTypeSystemPartGuid = { 0xC12A7328, 0xF81F, 0x11D2, { 0xBA, 0x4B, 0x00,
0xA0, 0xC9, 0x3E, 0xC9, 0x3B }}
gEf iPartTypeUnusedGuid = { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00 }}
当在模块工程文件的 [Guids] 中引用这些 Guid 时,这些值就会复制到 AutoGen.c 中。
5. [Protocols] 块
与 Guids 类似,在 Package\Include\Protocols 目录下有很多头文件,每个头文件定义了一
个或多个 Protocol,这些 Protocol 的 GUID 值就定义在 .dec 文件的 [Protocols] 区块,格式如下:
[Protocols.$(Arch)]
ProtocolName = GUID
例如,在 MdePkg\Include\Protocol 目录下的 BlockIo.h 定义了 BlockIo Protocol。
extern EFI_GUID gEf iBlockIoProtocolGuid;
gEf iBlockIoProtocolGuid 的值就定义在 MdePkg.dec 的 [Protocols] 块内。
[Protocols]
gEf iBlockIoProtocolGuid = { 0x964E5B21, 0x6459, 0x11D2, { 0x8E, 0x39, 0x00,
0xA0, 0xC9, 0x69, 0x72, 0x3B }}