综述
DSC的全称就是EDK Platform Description。dsc包含了模块,变量定义,库函数,PCD等内容,此外还包含编译选项,它的目的就是指定需要编译的内容,以及编译的参数。
本文参考《edk-ii-dsc-specification.pdf》(以下简称参考文档)。它可以在https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Specifications下载到。本文中使用到的代码示例来自vUDK2018: https://github.com/tianocore/edk2.git Tag vUDK2018.。
语法
本节介绍dsc文件的大致语法。
基本语法
先简单介绍一些fdf文件中使用到的基本语法:
1. 注释使用#;
2. 使用=设置常量,使用DEFINE设置宏,但是DEFINE语句中还是有=;
3. 可以使用整型,布尔值,EFI_GUID等值;
4. $()获取DEFINE语句定义的宏的值;
基本语句
DEFINE语句,定义一个宏:
DEFINE SECURE_BOOT_ENABLE = FALSE
定义的宏可以通过$()来访问,下面是一个例子:
!if $(SECURE_BOOT_ENABLE) == TRUE
PlatformSecureLib|OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.inf
TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf
AuthVariableLib|SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf
!else
TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
!endif
注意这里定义的宏也可以在fdf文件中使用。
!if语句:判断语句,为TRUE时才包含其内部的组件,其语法如下:
!if $(MACRO) 或者 !if $(MACRO) == "Literal String" 或者 !if $(MACROALPHA) == $(MACROBETA) 或者 !if $(MACRONUM) == 数字 或者 !if $(MACROBOOL) == 布尔值或者!if 布尔值。
注意需要与!endif语句一起使用,中间也可以有!else语句,上面已经有例子说明。其它类似的还有!ifdef语句和!ifndef语句。
Section
Section的大致格式如下:[oo.xx.zz]。这里oo是必选的,而xx、zz等需要根据oo的值来确定是否存在以及具体是什么内容。下面就介绍这些常用的Section关键字。
[Defines]
定义在这里的宏或者常量在dsc文件和fdf文件都是有效的,且有些值是需要传递给编译和生成工具的,因此这个Section是dsc中必须的。下面是一个例子:
[Defines]
PLATFORM_NAME = LearnUefi
PLATFORM_GUID = 5a9e7754-d81b-49ea-85ad-69eaa7b1539b
PLATFORM_VERSION = 0.1
DSC_SPECIFICATION = 0x00010005
OUTPUT_DIRECTORY = Build/LearnUefiPkg
SUPPORTED_ARCHITECTURES = X64
BUILD_TARGETS = NOOPT|DEBUG|RELEASE
SKUID_IDENTIFIER = DEFAULT
FLASH_DEFINITION = LearnUefiPkg/LearnUefiPkg.fdf
#
# Defines for default states. These can be changed on the command line.
# -D FLAG=VALUE
#
DEFINE SECURE_BOOT_ENABLE = FALSE
DEFINE NETWORK_IP6_ENABLE = FALSE
DEFINE HTTP_BOOT_ENABLE = FALSE
DEFINE SMM_REQUIRE = FALSE
其中有一些值比较重要,在这里说明:OUTPUT_DIRECTORY定义了编译生成的文件的输出目录,FLASH_DEFINITION定义了对应的fdf文件。
[BuildOptions]
这个Section用来指定编译选项。EDK可以在Windows、Linux和Mac上编译,所以这里的宏也可以指定不同的系统,且EDK包含很多的语言,所以也可以指定。此外,比较特别的一点是,BIOS包含的模块有不同的类型,不同类型会对应不同的体系架构,比如PEIM需要的是32位的编译,DXE之后需要64位的编译,等等。所以这个Section还有一些变种:
[BuildOptions]
[BuildOptions.common]
[BuildOptions.$(ARCH)]
[BuildOptions.common.CodeBase]
[BuildOptions.$(ARCH).CodeBase]
[BuildOptions.$(ARCH).CodeBase.$(MODULE_TYPE)]
这里其实是一个从通用到特定类型的范围缩小的过程,后者可以覆盖前者的定义。ARCH的值可以是X86、IA32等值。CodeBase对于我们想在的代码就是EDKII;MODULE_TYPE对应到的类型如下:
下面是一个例子:
[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
#
# Disable deprecated APIs.
#
MSFT:*_*_*_CC_FLAGS = /D DISABLE_NEW_DEPRECATED_INTERFACES
INTEL:*_*_*_CC_FLAGS = /D DISABLE_NEW_DEPRECATED_INTERFACES
GCC:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
[BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER]
GCC:*_*_*_DLINK_FLAGS = -z common-page-size=0x1000
XCODE:*_*_*_DLINK_FLAGS =
这里的[BuildOptions]相当于一个通用的配置,而之后的[BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER]是对一些特性的覆盖。
关于[BuildOptions]中的语句,格式如下:编译器:编译选项名=编译选项。其它的两个部分没有什么好多说的,重点是中间的编译选项名,它的一个例子:
RELEASE_UNIXGCC_IA32_CC_FLAGS
这里第一个值表示是Release还是Debug版本;第二个值是具体的编译工具,它在tools_def中的Supported Tool Chains中说明;第三个参数是架构类型;第四个是表示用于编译过程还是链接过程。
[SkuIds]
这个Section是可选的,实际上用处不大,这里不做说明。
[LibraryClasses]
这个Section定义了所以使用到的库函数。由于库可以使用在不同的阶段和架构,所以它也分为不同的子类,如下所示:
[LibraryClasses]
[LibraryClasses.common]
[LibraryClasses.$(ARCH)]
[LibraryClasses.common.$(MODULE_TYPE)]
[LibraryClasses.$(ARCH).$(MODULE_TYPE) ]
同样,这里也是越精细的范围覆盖越通用的范围。下面是一个例子:
[LibraryClasses]
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
[LibraryClasses.common]
BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
[LibraryClasses.common.SEC]
TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
QemuFwCfgLib|OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSecLib.inf
关于[LibraryClasses] 中的语句,格式如下:库名称|库路径。库名称跟对应在inf中的一样,路径从代码根目录开始。
[PcdsXXX]
PCD的Section有不同的种类,对应不同的PCD类型,如下所示:
[PcdsFeatureFlag]
[PcdsFixedAtBuild]
[PcdsDynamicDefault]
[PcdsPatchableInModule]
实际上PCD是在dec文件中定义和初始化的,在dsc文件中实际上是重新赋值,如果没有这么做,那该PCD就使用dec中的默认值。下面是一个例子:
[PcdsFeatureFlag]
gEfiMdeModulePkgTokenSpaceGuid.PcdHiiOsRuntimeSupport|FALSE
gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeUseSerial|FALSE
gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeUseMemory|TRUE
关于PCD这类Section中的语句,格式如下:GUID名.PCD名|PCD值。
[Components]
该Section中包含所以需要编译的模块,同样由于模块的不同,这里也会对应不同的变种:
[Components]
[Components.common]
[Components.$(ARCH)]
这里的ARCH可以有IA32、X64、ARM等值。下面是一个例子:
[Components]
OvmfPkg/ResetVector/ResetVector.inf
#
# SEC Phase modules
#
OvmfPkg/Sec/SecMain.inf {
<LibraryClasses>
NULL|MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf
}
这里的语句可以直接是一个路径,还可以在路径之后加上对库的覆盖,当然不止是库的覆盖,PCD和编译选项等也是可以覆盖的。