综述

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和编译选项等也是可以覆盖的。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐