読者です 読者をやめる 読者になる 読者になる

Converg.

ものづくり関係の備忘録

【EDK II】EDK II Package

EDK IIに関する備忘録第二弾。

プロローグ

EDK II Packageは,繰り返しますが,種々のModuleと関連した定義のセットを含んだコンテナです。
それぞれのPackageはEDK II ディストリービューションの単位です。
それらは,ユーザーのディストリービューションやリユースを促進するために,大きなプロジェクトをリリースしたりマネジメントするために使用されます。
全体のプロジェクトソースは,リリースの粒度(granularity)を低減させるため,異なるPackageに分割されます。新たなプロジェクトは異なるソースからリリースされたPackageを使用して作成されます。

EDK II Package

Packageは,DECファイルを併せたModuleのグループをまとめるディレクトリです。

EDK IIはUEFIやPIに準拠したPackageを提供しています(例えば,MdePkgやMdeModulePkgなど)。

MdePkgはEFI1.1,UEFI2.0,UEFI,2.1,PI1.0のスペックを完全な定義と,EDK II MDE(Module Development Environment) Library Specificationにより定義されるLibraryクラスとインスタンスを含んでます。UEFIドライバとPIドライバは,このPackageのみを元に開発できます。

EDK II Packageの詳細な情報は「EDK II User Guide」のSection 2.2にあり,それぞれのPackageのPackage specificationの中に書いています。

Packageディレクト

それぞれのPackageは,異なるソースファイルを分離するためにディレクトリ構造を持ちます。
ルートディレクトリにはInclude,Library,Application,Driversディレクトリがあります。

・Includeディレクトリには,このPackageや他のPackageで使用されるすべてのPublicヘッダファイルがあります。Includeディレクトリ以下には,Ppi,Protocol,Guid,Libraryクラスのヘッダファイルを作成するためのサブディレクトリがあります。
・Libraryディレクトリは,それぞれのライブラリインスタンスModuleを含んでいます。
・Applicationディレクトリは,それぞれのUEFIアプリケーションを含んでいます。
・Driverディレクトリは,ドライバやドライバのグループを含んでいます。

上記の(Libraryインスタンス,Application,Driver)Moduleは,それぞれソースファイルを置く自身のディレクトリを持っています。Moduleはそのディレクトリだけに依存するかもしれませんし,Publicヘッダファイルに依存するかもしれませんしまちまちです。Moduleのソースファイルは,別のModuleのディレクトリにあるソースファイルであってはいけません。

Packageの中のディレクトリ・サブディレクトリのサンプル

・ Package.des -------------> PackageのDECファイル
・ Package.dsc -------------> PackageのDSCファイル
・ Include -------------> Publicヘッダファイル
   ⚪︎ Protocol/ -------------> Public Protocolヘッダファイル
   ⚪︎ Ppi/ -------------> Public PPI(Peim to Peim Interface)ヘッダファイル
   ⚪︎ Guid/ -------------> Public GUIDヘッダファイル
   ⚪︎ IndustryStandard/ -------------> Public Industry Standardヘッダファイル
   ⚪︎ Library -------------> Public Libraryクラスヘッダファイル
・ Library -------------> Libraryインスタンス
   ⚪︎ NameOneLib/ -------------> ライブラリインスタンスNameOneソースとINFファイル
   ⚪︎ NameTwoLib/ -------------> ライブラリインスタンスNameTwoソースとINFファイル
・Application/ -------------> UEFIアプリケーション
   ⚪︎ NameOneApp/ -------------> アプリケーションNameOneソースとINFファイル
   ⚪︎ NameTwoApp/ -------------> アプリケーションNameTwoソースとINFファイル
・NameOneDxe/ -------------> DXEドライバNameOneのソースとINFファイル
・NameTwoPei/ -------------> PEIドライバNameTwoのソースとINFファイル

上記中,仮にPackageの中に関係したソースファイルがない場合は,対応したディレクトリはありません。例えば,もしPackageの中にアプリケーションがない場合は,わざわざからのディレクトリを作る必要はありません。

Package Declaration (DEC) File

それぞれのPackageは,パッケージのパブリックインターフェースを定義するため,一つのDECファイルを含んでいます。パブリックインターフェースとは,パッケージのパブリックヘッダファイル,GUID,PCDです。

DECには,[Defines],[Includes],[LibraryClass],[Guids],[Ppis],[Protocol],[Pcds]のセクションがあります。

[Defines]セクション:
Packageの名前やGUIDなどを定義します。

[Includes]セクション:
パブリックヘッダファイルディレクトリのルートディレクトリをリスト化しないといけません。

[LibraryClass]セクション:
Package/Include/Library ディレクトリ以下にあるライブラリヘッダファイルを含んでいます。
基本的にはそのパスを指定します。

[Guids]セクション:
Package/Include/Library ディレクトリ以下のGUIDについて,GUIDの値を指定します。

[Ppi]セクション:
Package/Include/Ppi ディレクトリ以下のPPIについて,GUIDを指定します。

[Protocols]セクション:
Package/Include/Protocol ディレクトリ以下のProtocolについて,GUIDを指定します。

PCDは,そのタイプ(FeatureFlag,FixedAtBuild,PatchableInModule,Dynamic,DynamicEXなど)によって異なるPCDセクションで宣言しています。PCDが多数のPCDタイプをサポートする場合,そのサポートされたセクションで宣言されません。PCDが宣言されるとき,そのデータ型とデフォルト値が指定されなければなりません。

下記は,DECファイルのサンプルです。追加で情報が加えられたりします。

Example:Package.dec

[Defines]
 DEC_SPECIFICATION = 0x00010005
 PACKAGE_NAME = パッケージ名
 PACKAGE_GUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 
 PACKAGE_VERSION = 0.1

[Includes]
 Include # パッケージをincludeするディレクトリ

[LibraryClasses]
 ## Libraryクラス名はLibraryヘッダファイル名と同じ
 OneClassLib | Include/Library/OneClassLib.h

[Guids]
 #GuidCName = {xxxxxxxx, xxxx, xxxx, {xx, xx, xx, xx, xx, xx, xx, xx}},

[Ppis]
 #PpiGuidName = {xxxxxxxx, xxxx, xxxx, {xx, xx, xx, xx, xx, xx, xx, xx}},

[Protocols]
 #ProtocolGuidName = {xxxxxxxx, xxxx, xxxx, {xx, xx, xx, xx, xx, xx, xx, xx}},
[PcdsFeatureFlag]
 #FeatureFlag PCDはBool型なので,値は TRUE か FALSE です。
 #PcdTokenSpaceCGuidName.PcdName | TRUE | BOOLEAN | TokenNumber
 #PcdTokenSpaceCGuidName.PcdName | FALSE | BOOLEAN | TokenNumber

[PcdsFixedAtBuild]
 #PcdTokenSpaceCGuidName.PcdName | DefaultValue | DataType | TokenNumber

[PcdsPatchableInModule]
 #PcdTokenSpaceCGuidName.PcdName | DefaultValue | DataType | TokenNumber

[PcdsDynamic]
 #PcdTokenSpaceCGuidName.PcdName | DefaultValue | DataType | TokenNumber

[PcdsDynamicEx]
 #PcdTokenSpaceCGuidName.PcdName | DefaultValue | DataType | TokenNumber

DECファイルフォーマットの詳細は,「EDK II DEC File Specification」を参照してください。

Package Description(DSC) File

それぞれのPackageはDECファイルとは別にDSCファイルを作成します。
すべてのModuleは,コンパイルとPackageに所属することの証明のため,DSCファイル内に記述されます。
DSCには,[Defines],[LibraryClass],[PCD],[Components]などのセクションがあります。

[Defines]セクション:
ビルド関連の情報をセットします。例えば,ビルドのアウトプットディレクトリ,ビルドターゲット,GUID,そしてビルドアーキテクチャです。

[LibraryClass]セクション:
[Components]セクションに記載されたドライバ・アプリケーションに使用されるLibraryクラスのインスタンスを指定します。

[PCDs]セクション:
[Components]セクションに記載されたModuleによって使用されるPCDタイプと値を構成します。
PCDの値がDECのデフォルト値と等しい場合やPCDタイプが特定の要件を必要としない場合,
PCDはDSCファイルの中で構成されません。その値とタイプはDECファイルの設定がデフォルトになります。
もしすべてのPCDがDSCファイル内で必要とされない場合,[PCDs]セクションは生成されません。

下記は,DSCファイルのサンプルです。情報が追加されたりする可能性があります。

Package.dsc

[Defines]
 PLATFORM_NAME = パッケージ名
 PLATFORM_GUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
 PLATFORM_VERSION = 0.1
 DSC_SPECIFICATION = 0x00010005
 OUTPUT_DIRECTORY = Build/パッケージ名
 SUPPORTED_ARCHITECTURES = IA32 | IPF | X64 | EBC
 BUILD_TARGET = DEBUG | RELEASE
 SKUID_IDENTIFIER = DEFAULT

[SkuIds]
 0 | DEFAULT #エントリ: 0 | DEFAULTは予約されていて必要

[LibraryClasses]
## もし,[Components]セクション記載のComponentsがより多くのライブラリクラスが使った場合,
## より多くのライブラリインスタンスが必要です。
## library class name | library instance INF file path from package
 DebugLib | MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf
 BaseLib | MdePkg/Library/BaseLib/BaseLib.inf
 BaseMemoryLib | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
 ......

## PCDsセクションは指定されていません。
## なので,すべてのPCDの値はDEC内の値がデフォルトになっています。
## [PcdsFeatureFlag]
## [PcdsFixedAtBuild]

[Components]
## すべてのライブラリ,ドライバ,アプリケーションはここに追加されコンパイルされます。
## PackageディレクトリからModule INFファイルまでのパスを指定します。
 PackageNamePkg/Library/NameOneLib/NameOneLib.inf
 PackageNamePkg/NameOneDxe/NameOneDxe.inf
 PackageNamePkg/NameTwoPei/NameTwoPei.inf

DSCファイルフォームの詳細は「DSC Specification」を参照してください。


Package管理

Packageの作成

今のPackageが要件を満たさない場合や,オリジナルのコードベースがEDK II Packageを分割する場合,新たなPackageが作成されます。新たなPackageを定義するための推奨されるルールは次のようになります。

・同じ機能に関連するModuleはすべて同じPackageに入っているべきです。例えば,異なるチップセットのために異なるPackageが用意されるべきです。
・異なるプラットフォームで共有される一般的なModuleは,別の一つのPackageの中にあるべきである。例えば,MdePkgやMdeModulePkgは共有されています。
・Moduleはリリース要件によってPackageにおさまっていくべきです。もしModuleが特定のカスタマーにだけリリースされる場合,それらのModuleは特定のPackageにおさまっているべきです。

なお,新たなPackageのソースファイルに制限は何もありません。たとえPackageに一つのファイルしかない場合でも,PackageとしてはOKです。
上述したルールに沿って,EDK IIはユーザーの参照用にいくつかのPackageを提供しています。
新たなPackageを追加するとき,次の手順を踏んで作成されます。

ディレクトリ作成の際,PackageNamePkgなどのように意味のあるPackage名を付けます。
・Package内容を記述するために,PackageのルートディレクトリにDECファイル及びDSCファイルを作成します。
・異なるソースファイルを置くため,Packageのサブディレクトリを作成します。

Packageの使用

Packageは,他のModuleやPlatformを開発するために必要なパブリックヘッダファイル,ライブラリクラス,PCD,そしてModuleを提供することができます。
Moduleは所属するPackageに依存していますが,それと同時に他のPackageに依存していることもあり得ます。
PlatformはそのPackage,あるいは別のPackage内にあるModuleによって作成されます。
EDK II Packageは基礎的な開発の単位です。Packageは開発環境を構成するために使用されます。開発要件次第で,開発環境はEDK II projectや他のソースから異なるPackageを導入できます。例えば,UEFIやPIドライバを開発したい場合,UEFI/PIの定義をすべて含むMdePkgが開発環境に必要になります。

以下はPackageに基づいて,どのようにModuleやPlatformを開発するかを示しています。

・PackageのDECファイルやIncludeディレクトリは,Packageのパブリックヘッダファイル・ライブラリクラス・PCDをリスト化します。新しいModuleが開発されたとき,それは今の開発環境ないのすべてのPackageのDECファイルの情報を含んでいます。もし,そのModuleが今の開発環境にないPackageの情報を欲する場合は,そのPackageを開発環境に加える必要があります。

・PackageのDSCファイルは,Packageによって提供されるModuleをリスト化します。開発者は,開発環境内で必要なモジュールを得る(,またそれらの情報をPlatformのDSCファイル内に記述する)ために,すべてのPackageのDSCファイルを検索していきます。そのあと,PlatformのDSCやFDFファイル内でそれらのModuleを記述します。もし新しいPlatformがまだ今の開発環境にないPackageを必要としているときは,そのPackageを追加する必要がありなす。

Packageのアップデート

PackageのDEC・DSCファイルは,このPackageのソースファイルによって生み出される特性を記述します。もしソースファイルが変更・削除・追加された場合は,DEC・DSCファイルもそれに合わせて変更しなければなりません。

DEC・DSCファイルに影響するソースコードの変更はすべて以下で紹介していきます。

1. PackageのIncludeディレクトリの更新

PackageのIncludeディレクトリが変更・削除・追加された場合,DECファイルの[Include]セクションを更新せねばなりません。

Example: Incude section of Package.dec

[Includes]
 Include     # 既存のPackage Includeパス
 LocalInclude     # 新しいIncludeパスを追加

2. GUIDs,PPIs,Protocolsの更新

GUIDの値やPackageのPubulic GUIDヘッダファイルで定義されるGUID Global CNameが変更された時,DECファイルの[Guids]セクションは新たなGUIDの値やGUID CNameに対応するため更新されねばなりません。もし仮にpubilic GUIDヘッダファイルが削除されたとすると,このファイル中で定義されたGUIDはDECファイルの[Guids]セクションから削除しないといけません。また,もし仮に新たなGUIDヘッダファイルがPackage Public Includeディレクトリに追加されたとすると,この新たなGUIDの宣言と値の記載をDECファイルの[Guids]セクションにしないといけません。以上のように,GUIDヘッダファイルと同じように,GUIDの値がPPIヘッダファイル・Protocolヘッダファイルで変更された場合も,同様に[Ppis]あるいは[Protocols]セクションに変更を加えなければいけません。

Example: Guid section of Package.dec

[Guids]
 #gGuidCName = {00000000, 0000, 0000, {00, 00, 00, 00, 00, 00, 00, 00}}
 #上記から,例えば名前と数値を下記のように更新します。
 gNewGuidCName = {00000000, 0000, 0000, {00, 00, 00, 00, 00, 00, 00, 01}}

3. ライブラリクラスの更新

ライブラリクラスヘッダファイルの名前が変更された時,ライブラリクラスヘッダファイルの名前はDEC内の[LibraryClasses]セクションを更新して,新たなライブラリクラス名をヘッダファイルに反映させる必要があります。ライブラリクラス名の変更もまた[LibraryClasses]セクションの更新が必要になります。新たなライブラリクラスが導入された場合も,その名前とヘッダファイルをDEC内の[LibraryClasses]で指定する必要があるでしょう。

Example: LibraryClasses section of Package.dec
[LibraryClasses]
 #OneClassLib |Include/Library/OneClassLib.infを下記のように更新する。
 BaseMemoryLib | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf

4. PCDsの更新

PCDはPackageのDECファイルで宣言されますが,いずれのヘッダファイルとも関係ありません。しかしながら,Moduleのソースファイルはそれらを利用します。もしPCDがどのModule内にも存在しない場合,その宣言はDECファイルから削除されるべきです。また,そのPCDの設定がDSCファイルにあるはずなので,それも削除すべきです。
Moduleが新たなPCDを必要とするときは,Moduleの存在するPackageのDEC内でPCDを定義する必要があります。そのとき,DECファイルはPCDのタイプとデフォルト値を指定することになるでしょう。

Example: Package.dec

[PcdsFixedAtBuild]
 #新たにFixedAtBuild PCDの追加。下記のフォーマット
 #PcdTokenSpaceCGuidName.PcdName | DefaultValue | DataType | TokenNumber
 gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldTime | 1 | UINT32 | 0x40000005

5. Moduleの更新

Module(Library instance,drivers,Application)への変更は,依存しているヘッダファイル・ライブラリクラス・PCDを編集させることになります。そしてそれは,DSCファイルを更新させることにもなります。

もし,ModuleのINFファイル名が変更された場合,そのModuleを参照するDSCファイル内の記述も,変更した新たな名前に更新する必要があります。
もし,Moduleが完全に削除された場合,もうコンパイルされないのでDSCファイルから記載を削除します。
Packageに新たなModuleが追加されたとき,DSCファイル内に追加されてコンパイルされるようにすべきです。

Example: Package.dsc

[Components]
# ModuleのINFファイルへのパスを,Packageをルートディレクトリとして記載します。
 #PackageNamePkg/NameTwoPei/NameTwoDxe.infを下記のように更新
 MdeModulePkg/Application/HelloWorld/HelloWorld.inf