BIOS/UEFI基礎——DSC檔案

jiangwei0512發表於2020-09-30

綜述

DSC的全稱就是EDK Platform Description。dsc包含了模組,變數定義,庫函式,PCD等內容,此外還包含編譯選項,它的目的就是指定需要編譯的內容,以及編譯的引數。

本文參考《edk-ii-dsc-specification.pdf》(以下簡稱參考文件)。它可以在https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Specifications下載到。本文中使用到的程式碼示例來自https://gitee.com/jiangwei0512/vUDK2018.git

 

語法

本節介紹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和編譯選項等也是可以覆蓋的。
 

相關文章