在开始编写UEFI APP之前,我们需要先对UEFI包和模块的概念有个了解。
在edk2的根目录下,我们可以发现有很多*Pkg命令的目录,这些实际上都是各个不同的包,每个包中都是一组模块的集合,每个包中都有对应的描述文件(.dsc),声明文件(.dec)。当然如果这个包是用来生成固件Image或者Option Rom Image,那么这个包还需要包含一个.fdf文件(Flash Description File),此文件是用来生成固件Image,Option Rom Image或者可启动Image的。
包的编译过程包括:
build 命令利用.dsc和.dec以及一个或者多个.inf文件来编译Package
GenFW 命令用来生成固件,利用.dec和.fdf文件来生成一个目标Image
描述文件(.dsc)
.dsc文件用于编译Package,它包含了[Defines],[LibraryClasses],[Components]几个必须部分以及[PCD],[BuildOptions]等可选部分。
[Defines]
定义用于描述该包的全局变量,它必须是dsc文件的第一个描述项目,必须定义的宏变量如下:[Defines] PLATFORM_NAME = Ovmf PLATFORM_GUID = 5a9e7754-d81b-49ea-85ad-69eaa7b1539b PLATFORM_VERSION = 0.1 DSC_SPECIFICATION = 0x00010005 SUPPORTED_ARCHITECTURES = X64 BUILD_TARGETS = NOOPT|DEBUG|RELEASE SKUID_IDENTIFIER = DEFAULT
当然也有可选择定义的宏,如下列出两个常用的:
OUTPUT_DIRECTORY = Build/OvmfX64 FLASH_DEFINITION = OvmfPkg/OvmfPkgX64.fdf ......
[LibraryClasses]
该块中定义引用的Library,格式需要指定库的名字已经对应的.inf路径,这里定义的Library,可以被包中所有的Components所引用。[LibraryClasses] PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseAcpiTimerLib.inf PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf BaseLib|MdePkg/Library/BaseLib/BaseLib.inf SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
如上所示,Library的定义需要name和.inf文件路径,并且中间以一个|符号分割开。也可以通过[LibraryClasses.$(ARCH).$(MODULE_TYPE)]来区分不同的Arch和Module Type。
[Components]
列出本包中所有的模块组件,各个模块是通过指定对应的.inf文件来加入到包中的:[Components] OvmfPkg/ResetVector/ResetVector.inf # SEC Phase modules OvmfPkg/Sec/SecMain.inf { <LibraryClasses> NULL|MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf } # PEI Phase modules MdeModulePkg/Core/Pei/PeiMain.inf MdeModulePkg/Universal/PCD/Pei/Pcd.inf { <LibraryClasses> PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf } MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPei.inf { <LibraryClasses> PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf }
如上所示,除了能够指定对应的模块inf文件,还可以通过{}来包含该模块的一些私有属性,比如在{}中定义的,这里的库是仅给父模块引用的,对包中其他的模块没有影响,而.dsc中定义的[LibraryClasses]块,则整个包中的模块都可以引用,如果模块私有引用了相同库名,但不同路径下的库,那么会覆盖公共区定义的引用。
[BuildOptions]
定义编译选项,此处定义的编译选项对该包中的所有模块都有效,格式如下:[BuildOptions.$(Arch).$(CodeBase)] [编译器]:[$(Target)]_[Tool]_[$(Arch)]_[CC|DLINK]_FLAGS=
实例:
[BuildOptions] GCC:*_UNIXGCC_*_CC_FLAGS = -DMDEPKG_NDEBUG GCC:RELEASE_*_*_CC_FLAGS = -DMDEPKG_NDEBUG INTEL:RELEASE_*_*_CC_FLAGS = /D MDEPKG_NDEBUG MSFT:RELEASE_*_*_CC_FLAGS = /D MDEPKG_NDEBUG
PCD Section
用于定义平台配置数据,其中定义的变量是可以直接被源程序通过接口获取到,其目的是在不改动.inf文件和源文件的情况下完成对平台的配置。这样就达到灵活配置平台选项,不用为了兼容不同平台而去修改代码,[PcdsFeatureFlag],[PcdsFixedAtBuild], [PcdsDynamicDefault]这三个都属于PCD Section的可选部分。
声明文件(.dec)
声明文件用来定义公开的数据和接口,供其他模块使用,包含必需块[Defines]以及可选块[Includes],[LibraryClasses],[Guids],[Protocols],[Ppis],[PCD]。.dec文件需要被调用模块.inf文件所包含。
[Defines]
提供包的名字,版本号,GUID:[Defines] DEC_SPECIFICATION = 0x00010005 PACKAGE_NAME = OvmfPkg PACKAGE_GUID = 2daf5f34-50e5-4b9d-b8e3-5562334d87e5 PACKAGE_VERSION = 0.1
[Includes]
头文件所在目录,起始于.dec所在目录的相对目录:[Includes] Include
[LibraryClasses]
该包对外提供的库的集合,格式为:库名 | 头文件[LibraryClasses] LoadLinuxLib|Include/Library/LoadLinuxLib.h
[Guids]
在Package/Include/Guid目录中有很多文件,每个文件内定义了一个或者多个GUID,例如OvmfPkg/Include/Guid/OvmfPlatformConfig.h头文件中:extern EFI_GUID gOvmfPlatformConfigGuid;
可以看到这里仅仅是一个声明,那么它的定义就是在本块中完成的:
[Guids] gOvmfPlatformConfigGuid = {0x7235c51c, 0x0c80, 0x4cab, {0x87, 0xac, 0x3b, 0x08, 0x4a, 0x63, 0x04, 0xb1}}
[Protocols]
和Guids配置方法类似,在Package/Include/Protocols目录定义了Protocol的头文件,比如头文件/OvmfPkg/Include/Protocol/VirtioDevice.h:extern EFI_GUID gVirtioDeviceProtocolGuid;
头文件仅仅是声明,定义在dec文件中:
[Protocols] gVirtioDeviceProtocolGuid = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}}
[Ppis]
定义源文件中用到的PPI(PEI模块之间通信的接口),语法类似Protocols。[PCD]
此块是对dsc文件中[PCD]块的补充。