Converg.

ものづくり関係の備忘録

【EDK II】 .UNI File Format

第四弾・・・。眠い。備忘録。

概要

Unicode string fileのフォーマットについての備忘録。
このファイルは,Unicodeファイル内で複数のレイアウトとフォーマットをサポートする。この多彩さが,言語または文字識別子によって,文字をグルーピングすることを可能にする。

Unicode String File Format

EDK II Unicode fileはマッピングされたトークン名に,RFC4646言語コードによって識別される文字をローカライズできる。
EDK II Unicode fileのフォーマットはUTF-16LE。文字内容はUCS-2でなければらない。

文字列の末尾は,次のうち最初に見つかったもので決定される。

・制御文字
・コメント
・ファイルの終わり
・空白行

String File内でコメントはどこにあっても良い。すべてのファイルは,Unicode BOM(Byte Order Mark)文字で始まらなければならない。
従って,編集する際は,UTF-16LEファイル内でUCS-2文字列をサポートするエディタを使用する必要がある。

Common EBNF

以下のEBNFは,UCS-2文字列リテラルを表現するため,(ダブル)クオテーションマーク文字を使用している。下記でセミコロンはコメントである。
(一部上付き文字を"^"つけて書いているので注意)

<US> ::= " "

<Letter> ::= { (\u0041-\u005A) }     ; 文字 A - Z
             { (\u0061-\u007A) }     ; 文字 a - z

<Digit> ::= (\u0030-\u0039)     ; 文字 0 - 9 

<MS> ::= <US>+

<ME> ::= {<MS>} {<EOL>}

<CommentLine> ::= "//" <US>* <PCHars> <EOL>

<BlankLine> ::= <EOL>

<Chars> ::= (\u0001-\uF6FF)

<PChars> ::= { (\u0020-\uF6FF) } {<OpChar>}

<OpChars> ::= "\x" [{<Letter>} {<Digit>}] {4} "\"

<VChars> ::= (\u0021-\uF6FF)

<UnicodeLines> ::= <Token> <Me>
                   [<Ldef> [<String> <ME>]+]+

<Ldef> ::= <CtrlChar> "language" <MS> <LangCode><ME>

<HexDigitU> ::= {<Digit>}
                { (\u0041-\u0046) }     ; 文字 A - F
                { (\u0061-\u0066) }     ; 文字 a - f

<CtrlChar> ::= <US>* "#"

<Token> ::= <CtrlChar> "string" <MS> <Identifier>

<Identifier> ::= <Letter> [{<Letter>} {<Digit>} {<UN>}]*

<LangCode> ::= <RFC4646>

<RFC4646> ::= <Letter>^(2,8) [<ShortExt> <LongExt>*]

<ShortExt> ::= "-" [{<Letter>} {<Digit>}]^(1,8)

<LongExt> ::= "-"  [{<Letter>} {<Digit>}]^(1, )

<UDblQuote> ::= \u0022     ; ダブルクオテーション文字「”」

<String> ::= <UDblQuote> <SContent>* <UDblQuote>

<SContent> ::= {<PChars>} {<Attributes>}

<Attributes> ::= "\" {"narrow"} {"wide"} {<UDbleQuote>}
                 {"n"} {"r"} {"t"} {"nbr"} {"\"} {"'"}
定義

LanguageCodes:
言語コードは有効なRFC4646言語コードでなければならない。

EscChar:
文字列中の"\"など,いくつかの標準文字列をインクルードするために,文字列はエスケープ文字を接頭辞としていなければならない。エスケープ文字を接頭辞に必要な文字は次のものを含む;バックスラッシュ「\」,クオテーションマーク「'」,ダブルクオテーションマーク「"」,スラッシュ「/」。
バックスラッシュはいつもエスケープ文字を必要とする。

Token:
トークンは数字,上付き・下付き文字,下線文字,ダッシュ文字を含む。

Include:
include行は,文字ファイル内にあった場合,この仕様に準拠した別ファイルを構文解析するのに用いられる。トークンは同じ言語のためのファイルで重複してはならない。


HII String Pack

HII String Packの作成に利用されるUnicodeファイルのフォーマット:

<StringFileFormat> ::= <CommentLine>*
                       <LanguageDefs>
                       <Content>+

次のEBNFは,HII String Pack作成に使用されるUnicodeファイルに特化した内容を記載している。

<Content> ::= {<CommentLine>} {<BlankLine>}
              {<UnicodeLines>} {<ControlRefactor>}
              {<LanguageDefs>} {<SecurityLines>}
              {<IncludeLines>}


HII String Pack作成に使用するUnicodeファイルの他の定義は下記の通り。

<LanguageDefs> ::= <CtrlChar> "langdef" <MS> <LangCode> <MS>
                   <LangDesc> <EOL>

<LangDesc> ::= <UDblQuote> <Chars> <UDblQuote>

<IncludeLines> ::= <CtrlChar> "include" <UniFile> <EOL>

<UniFile> ::= <UDblQuote> <UniFilename> <UDblQuote>

<UniFilename> ::= <FilenameChars> <MoreFNameChars>* {".uni"}

<FilenameChars> ::= {<Letter>} {<Digit>}

<MoreFNameChars> ::= {<Letter>} {<Digit>} {"_"}

<CtrlChar> ::= "/"

<ControlRefactor> ::= <CtrlChar> "=" <NewCtrlChar> <EOL>

<NewCtrlChar> ::= (0x0021 - 0xF6FF)

サンプルファイル

UEFIのUNI Spec v1.3より引用。

//
// Cpu I/O Strings
// Copyright (c) 2006, Intel Corporation. All rights reserved.<BR>
//
// This program and the accompanying materials are licensed and made
// available under the terms and conditions of the BSD License which
// accompanies this distribution. The full text of the license may
// be found at:
// http://opensource.org/licenses/bsd-license.php
//
// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS
// OR IMPLIED.
//

/=#

#langdef     en-US   "English, US"
#langdef     fr-FR   "Français"

#string     STR_PROCESSOR_VERSION
#language  en-US
"NT32 Emulated Processor"
#language fr-FR 
"Processeur Émulé par NT32"


 

フォントサポート

フォントをサポートするためのEDK II Unicodeファイルの,任意の特徴とエントリをここで定義する。

Syntax:
「#fontdef」・「#font」コマンドの導入,「#string」コマンドを延長,新しいエスケープ文字を追記することで,フォントを拡張する。

Fonts:
との文字列もフォントと関連がある。フォントには,フォントフォント識別子,フォント名,サイズ,スタイルがある。デフォルトでは,文字列はフォント識別子"sysdefault"と関連づけられている。普通これは,標準的なUEFIフォントの19, normalとなっている。

文字列は,最初の #language 属性の前に #string 属性を使用することによって,異なる #font 識別子を使うことが可能になる。

特定の言語の文字列だけ異なる#font属性を使用したい場合は,#language属性の後に#font属性を使用すれば良い。

文字列内の文字も,異なるフォント識別子,フォントサイズフォントスタイルを使用することができる。そのためには,下記の \f エスケープシークエンスを利用すれば良い。これらのエスケープ文字は,EDK2 Build Specificationに記載のものを拡張することができる。

Font Control Character Description
\" ダブルクオテーションの挿入。
\\ バックスラッシュの挿入。
\br Breaking code。
\f!identifier! つぎにつづくもじのフォント識別子を選択する。
\fb 現在の文字列に続く文字を太文字スタイルに切り替える。
\fd 現在の二重下線スタイルに切り替える。現在のスタイルが下線の場合は,二重下線になる。
\fe 次に続く文字をエンボス文字に切り替える。
\fh!integer! 次に続くフォントのサイズを選択する。
\fi 現在の文字列に続く文字を,イタリック文字にする。
\fs 現在の文字列に続く文字を,影文字にする。
\fu 現在の文字列に続く文字を,下線文字にする。
\n キャリッジリターンと改行の挿入。
\narrow 次に続く文字を"狭い"文字にする。
\nbr Non-breaking code。
\r キャリッジリターンの挿入
\wide 次に続く文字を"広い"文字にする。

フォント識別子は#fontdefを使用することにより作成される。

 

#font

#stringsを使用してデフォルトフォントをセットする。

Syntax:

"#font" <MS> font-identifier

Attributes:
font-identifier
==> フォントに関連づけられたCスタイルの識別子。

 

#fontdef

フォント識別子を特定のフォントファミリー,サイズ,スタイルとかんれんづける。

Syntax:

"#fontdef" <MS> font-identifier <MS> <FontOptions> <EOL>

<FontOptions> ::= font-name <MS> font-size [<MS> font-style-list]

font-style-list ::= <UDblQuote> [fs-entries] <UDblQuote>

fs-entries ::= font-style ["|" font-style]*

font-style ::= {"bold"} {"italic"} {"underline"} {"dblunder"}
               {"shadow"} {"emboss"} {"normal"}

font-size ::= (1-9) (0-9)*

 
Attributes:
font-identifier
==> Cスタイルの識別子

font-name
==> フォントファミリー名をクオテーション文字で囲ったもの。例えば,"Arial"や"Times New Roman"など。

font-size
==> ピクセルでフォントたかさを指定するunsigned型のinteger。たとえば,UEFI標準フォントの高さは,19ピクセルなので,font-sizeは19。

font-style
==> フォントスタイルを指定する0以上のキーワード。"|"で仕切る。
仮に"normal"が使用された場合,他のどのフォントスタイルとも組み合わせられない。特に何も指定しなければ,"normal"と解釈される。

 
 

#stringの拡張

EDK II のビルドコマンドは,INFファイルの[Sources]セクションで指定された.uniファイルを構文解析する。ビルドツールは各々のModuleに対して,HII stringファイル中のシンタックスを,AutoGen.cファイル中のバイト配列変換するために,pythonオブジェクトを使用する。
バイト配列を作成するプロセスの詳細は,EDK II Build Specificationを参照すること。

Syntax:

<UnicodeLines> ::= "#string" <MS> <Identifier> <ME>
                   [<FontId>]
                   [<LangLine>]+

<LangLine> ::= "#language" <MS> lang-code <ME> <FontString>

<FontString> ::= [<FontId>] [<strings>]+

<FontId> ::= ["#font" <MS> <font-identifier> <ME>]

<strings> ::= <String> <ME>


[EDK II Specification中の#stringコマンドの拡張]:
font属性は文字列中の文字で使用されるデフォルトフォントを指定する。
もし,#fontが指定されなかった場合,デフォルトのフォント識別子が使用される。

もし,#font属性が#language識別子の前にあった場合,全ての言語にそのfont属性が適用される。
一方で,もし,#font属性が#language識別子の後にあった場合,その言語の文字列の文字にのみ,font属性が適用される。

#fontは一つ以上の箇所に書いてもいい。なお,その場合,言語のフォント識別子が優先される。
 
Description:
#fontdef コマンドはフォント識別子を導入し,それを特定のフォントファミリー,サイズ,スタイルと関連付ける。もし,すでにフォント識別子が定義されている場合は,新たな定義は無視される。

【EDK II】Module開発

引き続き第三弾。途中で力尽きて寝落ちすると思います。

EDK IIのModuleとは・・・?

EDK IIのModuleはソースファイルあるいはバイナリファイル,そしてModule定義ファイル(INFファイル)から構成されています。INFファイルはModuleの基本的な情報と,読み込む/生成するライブラリクラス・PCD・Protocol・PPI・GUIDといったインターフェースを記述しています(「EDK II Extended INF Specification」を参照下さい)。

典型的なEDK II Moduleは,ビルドされた後,FFS(Firmware File System)に置かれ,そしてFV(Firmware Volume) Imageに置かれたFirmware Componentです。
Componentとして考えられるのは次のようなものです。

efiバイナリファイルにビルドされ,EFI_PE_SECTIONとしてFFSファイルに置かれるドライバやアプリケーション
・Rawデータバイナリ。例えば,$(WORKSPACE)/MdeModulePkg/Logo/Logo.infはbitmapイメージのロゴを含むRaw Binary Moduleです。
・Device's Option ROMに置かれるOption ROMドライバ
スタンドアローンUEFIドライバ
スタンドアローンUEFIアプリケーション
・ライブラリオブジェクトファイル(.lib)にビルドされ他のModuleに静的にリンクされる,ライブラリインスタンス

ModuleはソースコードのままやEFIバイナリフォーマットでリリースされます。

Module Type

EDK IIは多くのModule Typeを定義しています。
Module typeの用途は次のようなものです。

・Moduleのライフサイクルを示します。
例えば,PEIMはPEI Phaseでディスパッチされます。また,DXE_DRIVERやUEFI_DRIVERはDXE Phaseでディスパッチされます。

・Moduleのバイナリイメージ生成を示します。
例えば,PEIM/DXE_DRIVER TypeのModuleは.efiバイナリイメージに"depex"セクションがあります。UEFI_DRIVERは.efiバイナリイメージに.uiや.verセクションがあります。

・Moduleの適切なライブラリインスタンスを示します。ライブラリインスタンスは,INFファイル内でどんなModule Typeがサポートされるか指示しています。

MODULE_TYPE DESCRIPTION
SEC このModuleはCPUのリセットベクタで実行を開始するよう設計されており,それらはPEI Phaseのためにプラットフォームを準備します。
SECのために定義された標準サービスはないので,このタイプのModuleは同じルールに従います。また,一般的には,スタック用Temporary Memoryを作るための特定のCPUアセンブリコードをいくつか含んでいます。
このModuleは,PEI PhaseにHOBs(Hand-Off-Blocks)という形式でパスされるサービスを生成します。なお,それらのサービスはPIの仕様に準拠していなければなりません。
PEI_CORE このタイプのModuleは,PIの仕様に準拠したPEI Coreの実行により使用されます。
PEIM このタイプのModuleは,PIの仕様に準拠したPEIMsによって使用されます。
DXE_CORE このタイプのModuleは,PIの仕様に準拠したDXE Coreの実行により使用されます。
DXE_DRIVER このタイプのModuleは,PIの仕様に準拠したDXEドライバに使用されます。
それらのModuleはブートサービス環境で実行されるだけでExitBootServices()がコールされた時にdestroyされます。
DXE_RUNTIME_DRIVER このタイプのModuleは,PIの仕様に準拠したDXEドライバに使用されます。
これらのModuleはブートサービス環境でもランタイムサービス環境でも実行可能です。つまりこれは,サービスがExitBootServices()がコールされた後でも使用可能ということです。もし,SetVirtualAddressMap()がコールされたら,このModuleはOSが提供する仮想アドレスマップに従って再配置されます。
DXE_SAL_DRIVER このタイプのModuleは,SetVirtualAddressMap()がコールされる前のPhysical Modeや,SetVirtualAddressMap()がコールされた後のPhysical ModeあるいはVirtual Modeで,DXEドライバに使用されます。このModuleはIPF CPUだけ実行可能です。つまりこれは,Moduleが生成するサービスがExitBootServices()がコールされた後でも実行可能ということです。
DXE_SMM_DRIVER このタイプのModuleは,SMRAMにロードされるDXEドライバに使用されます。結果としてこのModuleはIA32,x64のCPUで実行可能です。これらのModuleはPhysical Modeでのみ実行され,destroyされることは決してありません。つまりこれは,Moduleが生成するサービスがExitBootServices()がコールされた後でも実行可能ということです。
UEFI_DRIVER このタイプのModuleは,UEFIの仕様に準拠したUEFIドライバに使用されます。これらのModuleはBootService実行環境でサービスを提供します。EFI_SUCCESSを返すUEFIドライバはメモリからアンロードされません。一方,errorを返すUEFIドライバはメモリからアンロードされます。
UEFI_APPLICATION このタイプのModuleは,UEFIの仕様に準拠したUEFIアプリケーションに使用されます。UEFIアプリケーションはそれらが終了した時,いつもメモリからアンロードされます。

 

Moduleの作成

ドライバやライブラリModuleは,下記に似たような手順に沿って作成されます。

1. Moduleを置くPackageを作成・選択する。
2. Module用のディレクトリを作成してディレクトリにINFファイルを置く。
3. INFファイルにPackageの依存情報を追記する。
4. INFファイルにPPI,Protorol,GUID,PCDあるいはライブラリクラスの依存情報を追記する。
5. もし,このModuleがPPI,ProtocolあるいはGUIDに依存しており,[depex]セクションをサポートしている場合,INFファイルに[depex]セクションを追記する。
6. ソースファイルを作成し,ソースファイルの相対パスをINFファイルに追記する。


位置

ModuleはリリースされるとPackage内に配布されるので,新しいModuleのために適切なPackageを作成したり選択することが最初のステップです。


Packageの選択

EDK IIのPackageは,似たような定義やModuleを含めるために使用されます。その”似たような”は,次のルールによって判断されるべきです。

Industry standard:
例えば,MdePkgは,PIWG,UEFI,SMBIOS,USB, PCIなど業界規範からの定義を含めています。

Similar technology:
例えば,OptionRomPkgは,Option ROM技術に関連する定義やModuleをグループ化しています。

Business reason:
例えば,IntelFrameworkPkgは,Intelフレームワーク実行のための定義やModuleをグループ化しています。

Platform:
例えば,Nt32PkgはNt32プラットフォームに必要な定義やModuleをグループ化しています。
さらに,Platform PackageはビルドのためにDSC・FDFファイルも提供しています。

Module開発の最初の段階では,Module開発者は適切なPackageを選択するために,目的やModuleのリリース過程を塾考する必要があります。


Moduleディレクトリの追加

Moduleディレクトリの適切なPackageへの追加は,次の方法でなされることが推奨されます。

・ライブラリModuleは"/Library"ディレクトリへ置くこと
・PROTOCOL,PPI,GUIDあるいはライブラリクラスの定義をそれぞれ,"/Include/Protocol","/Include/Ppi","/Include/Guid","/Include/Library"ディレクトリに置くこと
・ドライバModuleを""に置くこと
・アプリケーションModuleを"/Application"ディレクトリに置くこと
・次のようにModuleに推奨された名前を用いること

推奨されるディレクトリ名の慣習 Module Type
XxxPei PEIM,PEI_CORE
XxxDxe DXE_DRIVER,UEFI_DRIVER
XxxRuntimeDxe DXE_RUNTIME_DRIVER
XxxxDxeSal DXE_SAL_DRIVER
XxxxLib Library Instance


 

サンプル:Moduleメタファイル(INF)

Moduleは各々,ModuleのルートディレクトリにINFファイルが必要です。
Moduleは次のアイテムを含むINFファイル(たまにModuleメタファイルと言及される)
です。
・Module名,GUID,Module Typeなど,Moduleの基本情報
・Moduleが依存している任意のPackageのパス
・Module含まれているバイナリファイルあるいはソースファイルのパス
・Moduleに必要とされるProtocol,PPI,GUIDといったインターフェースすべてのリスト
・Moduleに必要とされるPCDやライブラリクラスすべてのリスト
・その他,Module Typeに依存するsectionなど

アプリケーションModule INFの例:

##
[Defines]
 INF_VERSION = 0x00010005
 BASE_NAME = HelloWorld
 FILE_GUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
 MODULE_TYPE = UEFI_APPLICATION
 VERSION_STRING = 1.0
 UEFI_SPECIFICATION_VERSION = 0x0002001E
 ENTRY_POINT = 0x0002001E

#
# 次の情報はリファレンスのみでビルドツールには必要とされていません。
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#

##
[Sources.common]
 Helloworld.c

##
[Package]
 MdePkg/MdePkg.dec

##
[LibraryClasses]
 UefiBootServicesTableLib
 UefiApplicationEntryPoint
 UefiLib
 DebugLib


下記はライブラリインスタンスPeiServicesTablePointerLib.infのサンプルINF:

[Defines]
 INF_VERSION = 0x00010005
 BASE_NAME = PeiServicesTablePointerLib
 FILE_GUID = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
 MODULE_TYPE = PEIM
 VERSION_STRING = 1.0
 LIBRARY_CLASS = PeiServicesTablePointerLib|PEIM
 PEI_CORE SEC
 CONSTRUCTOR = PeiServicesTablePointerLibConstructor
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC (EBC is for build only)
#
#
[Sources.common]
 PeiServicesTablePointer.c
[Packages]
 MdePkg/MdePkg.dec
[LibraryClasses]
 DebugLib 


 

Package依存情報の追加

INFファイルの[Packages]セクションは,このModuleのPackageへの依存情報をすべて記載します。依存するPackageのDECファイルのパスが,INFファイルの[Packages]セクションに記載されることになります。例えば下記のようになります。

[Packages]
# パスは$WORKSPACEをルートとするパスなので注意してください。
 MdePkg/MdePkg.dec
 IntelFrameworkPkg/IntelFrameworkPkg.dec

ModuleはPackageの依存情報を決定するのに以下のルールに従うべきです。

・MdePkgはすべてのModuleに必要になります。
Intelフレームワークの仕様の定義を使用する場合は,IntelFrameworkPkgが必要になります。
・上記のルール以上に,Protocol,PPI,GUID,PCD,ライブラリクラスを参照することで,より多くのPackageへの依存があります。例えばもし,ModuleがMdeModulePackage内で定義される"HiiLib"クラスから,定義やインターフェースを参照すると,それは同時にMdeModulePkgに依存していることにもなります。


ソースファイルの追加

すべてのModuleのソースコードはINF内の[Sources]セクションに記載され,次のルールに基づいています。
・異なるアーキテクチャのソースは異なるソースセクションに記載されます。

[Sources.common] # すべてのアーキテクチャに有効
 CheckSum.c
...

[Sources.Ia32] # IA32アーキテクチャにのみ有効
 Ia32/Wbinvd.c | MSFT
 ...
 Ia32/WriteMm7.S | GCC
 ...

[Sources.X64] # X64アーキテクチャにのみ有効
 X64/Thunk16.asm
 ...

[Sources.IPF] # IPFアーキテクチャにのみ有効
 Ipf/AsmCpuMisc.s
 ...

[Sources.EBC] # EBCアーキテクチャにのみ有効
 Synchronization.c
 ...

・Tool Tagはツールチェーン別にソースを指定するために使用します。

[Sources.Ia32]
 Ia32/Wbinvd.c | MSFT # MSFTツールを使用したとき,このソースがビルドされます。
 ...
 Ia32/WriteMm7.S | GCC # GCCツールを使用したとき,このソースがビルドされます。
 ...
    "$(CC)" -o ${dst} $(CC_FLAGS) $(INC) ${src}

・すべてのファイルはModuleのメインフォルダ以下に置くこと。"./"の表現は使わないこと。
・すべてのCインクルードファイルは[Sources]セクション内にリスト化されるべきです。

サポートされているTool Tag

EDK IIでサポートされるTool Tagは下記の通りです。

Tool Tag DESCRIPTION
MSFT Microsoft Family Tool Chain
INTEL INTEL Tool Chain
GCC GCC Tool Chain


 

ライブラリクラスリファレンスの追加

ライブラリクラスはいくつかのマクロあるいは構造体定義と関数宣言を抽出します。ライブラリインスタンスは,異なるプラットフォームや一つのプラットフォーム内の異なるPhase(SEC,PEI,DXE)に対して,異なるものになります。それゆえ,ModuleはプラットフォームやPhase動作のライブラリクラスに依存しています。

Module内でライブラリクラスを使う手順は下記の通りです。

1. INF内に記載のライブラリクラスを含むPackageへの依存情報を追記します。
2. INF内に記載のライブラリクラスへの依存情報を追記します。
3. ソースコード中でライブラリクラスヘッダファイルをIncludeします。

C言語のソースファイル内でライブラリクラスヘッダをIncludeする際は,次のようなsyntaxを使用します。

#include <Library/OemHookStatusCodeLib.h>

ヘッダファイルのインクルードパスは,PackageのDEC内[Includes]セクションで定義されるPackageのパブリックインクルードパスからの相対で表します。


 

PCD(Platform Configuration Database)リファレンスの追加

マクロとグローバル変数は,異なるアーキテクチャや異なるプラットフォームでModuleをカスタマイズするために広く使用されます。EDK IIはこれらの手法を置き換えるPCDの概念を紹介しています。
例えば,"FeatureFlag"型のPCDは,いくつかの特徴や機能がPCDの値がTRUEの時に有効化されるという点で,マクロに似ています。逆の場合も同様です。

PCDエントリはPCDのToken Space GUID C Nameにより,"."とC Nameにより定義されます。一つのPCD Token Spaceの中で,PCDのC Nameは固有のものになります。

例えば,PCD gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevelを例に挙げてみると,gEfiMdePkgTokenSpaceGuidはToken Space名であり,PcdDebugPrintErrorLevelはPCDの名前です。なお,gEfiMdePkgTokenSpaceGuidはMdePkg内で定義されるGUIマッピングされています。

gEfiMdePkgTokenSpaceGuid = {0x914AEBE7, 0x4635, 0x459B, {0xAA, 0x1C, 0x11, 0xE2, 0x19, 0xB0, 0x3A, 0x10}}


 

PCDのType

EDK IIは次のようなPCD Typeを提供しています。

Feature flag type PCD このタイプのPCDは,特徴を有効化・無効化するswitch MACROの代替です。これはBOOL型で値はTRUEもしくはFALSEです。
Fixed at build type PCD このタイプのPCDは,PCI Expressベースアドレスといったカスタマイズ可能な値を生成するマクロを代替します。
Patchable in module type PCD このタイプのPCDは,Fixed at build型のPCDにとても似ていますが,値はModuleのPEイメージのコードセクションではなくデータセクションに保存されています。
Dynamic type PCD このタイプのPCDは,他のタイプのPCDとは異なるものです。値は一つのModuleにより割り当てられ,実行中は他のModuleからアクセスされます。PEIMであるPcdPeimやDXEドライバであるPcdDXEは各々,それぞれのPhaseにおいて,使用するDynamic PCDの情報を含むデータベースを整備します。


 

Module INFファイルへのPCDの追加

PCDエントリを参考として引用するために,Token Space Guid NameとPCD NameがINFファイルの[PCD]セクションに追加されなければなりません。

[PCD.common]
 gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength
 gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength
 gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength

プラットフォームインテグレーターがいろんな用途で任意のPCDタイプを使用できるように,Module INFファイルでは一般的なタイプの"PCD"の使用が推奨されます。例えば,デスクトッププラットフォームでは,メモリー長が"Dynamic" PCDとして設計され,その値はMemory Discoveryドライバによって生成されます。しかしながら,いくつかの特殊な組み込みシステム内では,メモリー長が"FixedAtBuild"タイプのPCDとして設計され,その値はいつも固定されています。

PCD Typeを選択する際には制限があります。
もし,PCDの値が配列長のように定数として使用された場合,このPCDのタイプは"FixedPcd"であるべきです。例えば以下のような例があります。

UINT8 MySampleArray[FixedPcdGet16(PcdArrayLength)] = (0);

ライブラリインスタンスは異なるModuleにリンクしうるし,同一のPCDは異なるModuleで異なる値を持ちうるので,ライブラリインスタンスModuleには"FixedPcd"を用いることを避けてください。
単一のModuleでは,2つのPCDを同じ名前で使わず異なる,Token Spaceで使用してください。

PCD Type INF File Section Name
任意のPCD TypeにマッピングされるGeneral Type PCD
Feature Flag FeaturePcd
Fixed At Build FixedPcd
Patchable In Module PatchPcd
Dynamic PCD
CソースコードからPCDの値へのアクセス

ソースコードからPCDの値を得たりセットするには,次の手順にそうべきです。

1. Module INFファイルにPcdLibの依存情報を追記します。
2. MdePkgの依存情報を追記する。
3. ソースコード中にをインクルードする
4. PCDの値にアクセスするために,PcdLibを使用する

Function name INF PCD Section Name
PcdGetx()/PcdSetx() Common get/set function for all PCDs type
FixedPcdGetx() Get/set function for "FeaturePcd"
FixedPcdGetx() Get function for "FixedPcd"
PatchPcdGetx()/PatchPcdSetx Get/set function for "PatchPcd"

例えば,PcdGet32マクロは,PCDのすべてのTypeの値を,
32bit値で得るために使われます。

 //
 // Check driver debug mask value and global mask
 //
 if ((ErrorLevel & PcdGet32(PcdDebugPrintErrorLevel)) == 0){
  return;
 }

Protocol,PPI,GUIDの参照

Protocol,PPI,GUIDは,UEFIアーキテクチャインターフェースの細目であり,
ファームウェアの構成要素のインターフェースを抽出します。
以降は,Moduleからどのようにこれらのインターフェースを参照するのか見ていきましょう。

INFファイルへProtocol,PPI,GUIDの追加

Protocol,PPI,GUIDの名前は,Module INF内の[Protocol],[Ppi],[Guid]に追記する必要があります。例えば,次のようなものです。

[Protocol]
gEfiSampleProtocol


 

ヘッダファイルをソースコードにインクルード

Protocol,PPI,GUIDのヘッダファイルは似たような構造を定義します。ヘッダファイルのパスを探すには次の手順を踏んでください。

Ppiのヘッダファイルは,/Include/Ppi にあります。
・Protocolのヘッダファイルは,/Include/Protocol にあります。
・Guidのヘッダファイルは,/Include/Guid にあります。

ヘッダファイルはModuleソースコードに明確にに含まれなければなりません。
つまり下記のように宣言します。

#include <Protocol/DeviceIo.h>
#include <Ppi/Reset.h>
#include <Guid/GlobalVariable.h>

 

Moduleの依存情報を追加

依存情報は,ドライバのエントリポイントで実行したりディスパッチする条件を与えることになります。これは,PEIMやDXE Moduleにディスパッチする順番を決めるのに役立ちます。

Ppiのヘッダファイルは,/Include/Ppi にあります。
・Protocolのヘッダファイルは,/Include/Protocol にあります。
・Guidのヘッダファイルは,/Include/Guid にあります。

ヘッダファイルはModuleのソースコードに明確に含まれなければなりません。例えば,次のようになります。

#include <Protocol/DeviceIo.h>
#include <Ppi/Reset.h>
#include <Guid/GlobalVariable.h>



 

ライブラリインスタンスへの追加の手順

生成されるライブラリクラスの定義

ライブラリインスタンスは,常に単一のライブラリクラスに関連づけられており,ライブラリクラスにより定義されるすべてのインターフェースを実装します。それゆえ,ライブラリクラス名は,ライブラリインスタンスのINFファイル内の[Defines]セクションに記載しなければなりません。例えば次のように記載します。

[Defines]
 LIBRARY_CLASS = UefiDriverEntryPoint | DXE_DRIVER DXE_RUNTIME_DRIVER

上記では,UefiDriverEntryPointはライブラリインスタンスにより作られたライブラリクラス名です。さらに,"DXE_DRIVER DXE_RUNTIME_DRIVER"はライブラリインスタンスをサポートするModuleのTypeです。


 

ライブラリコンストラクターの定義(Optional)

ライブラリインスタンスModuleは,各々リンクするModuleのエントリポイントによって呼び出されるライブラリコンストラクター関数を定義することができます。ライブラリコンストラクター関数には,任意のライブラリインターフェースが使用される前にいくつかの初期化処理があります。

[Defines]
 ...
 CONSTRUCTOR = HobLibConstructor
ライブラリコンストラクター関数のType

ライブラリインスタンスのModule Typeによって,ライブラリコンストラクター関数は3つのタイプに分けられます。

・BASE module typeのライブラリインスタンス

EFI_STATUS
EFIAPI
BaseConstructor (
 VOID
 )

 
・PEIM,PEI_CORE module typeのライブラリインスタンス

EFI_STATUS
EFIAPI
PeiServiceTablePointerLibConstructor (
 IN EFI_PEI_FILE_HANDLE          FileHandle,
 IN CONST EFI_PEI_SERVICE          **PeiServices
 )

 
・DXE_DRIVER,DXE_CORE,DXE_RUNTIME_DRIVER,UEFI_APPLICATION,UEFI_DRIVER module typeのライブラリインスタンス

EFI_STATUS
EFIAPI
DxeCorePerformanceLibConstructor (
 IN EFI_HANDLE          ImageHandle,
 IN EFI_SYSTEM_TABLE          *SystemTable
 )


 

ライブラリデストラクターの定義(Optional)

ライブラリインスタンスModuleは,DXE_DRIVERやUEFI_DRIVERなどのExitDriver()によって呼び出される,ライブラリデストラクター関数を定義することができます。ライブラリデストラクター関数には,いくつか初期化されない処理があります。

デストラクター関数はINFファイル内で,次のように明確に宣言される必要があります。

[Defines]
 ...
 DESTRUCTOR = HobLibDestructor

デストラクター関数のプロトタイプは,上述したコンストラクター関数と同じです。


ドライバの手順のおまけ

ドライバのエントリポイントの定義

下記のようにPEIM,DXEドライバは,INFファイルの[Defines]セクションで定義されるエントリポイント関数を定義します。

[Defines]
 ...
 ENTRY_POINT = PcdDxeInit

Moduleエントリポイント関数は,そのModule Typeによって異なります。
詳細は下記のようになります。

Concept Description
PEIM Entry Point Library PEIMのモジュールエントリポイントライブラリ。
UEFI Driver Entry Point Library UEFIドライバ,DXEドライバ,DXEランタイムドライバ,DXE SMMドライバのモジュールエントリポイントライブラリ。
UEFI Application Entry Point Library UEFIアプリケーションのモジュールエントリポイントライブラリ。
PEI Services Table Pointer Library PEI Services Tableへのポインタを検索するサービスを提供します。
UEFI Boot Services Table Library EFI Boot Services Tableへのポインタを検索するサービスを提供します。DXEとUEFIのModule Typeのみ実行可能です。
UEFI Runtime Services Table Library EFI Runtime Services Tableへのポインタを検索するサービスを提供します。DXEとUEFIのModule Typeのみ実行可能です。
DXE Services Table Library DXE Service Tableへのポインタを検索するサービスを提供します。DXEのModule Typeのみ実行可能です。


 

HII(Human Interface Infrastructure)を使用するModule

DXE Moduleは,BDS Phase中にブラウザで使用される次のリソースを公開したり更新することができます。

Forms:
ユーザに映し出す必要のあるコンテンツのタイプを記述します。

Strings:
基本的にFormに参照される情報のテキストベース(UCS-2エンコード)表示。

Font/Image:
ローカル上でレンダリングされるコンテンツ。

HIIの詳細は,「UEFI Specification」27章 Human Interface Infrastructure Overviewを参照ください。

Forms

VFRリソースファイルの作成

VFRファイルはFormリソースを記述します。VFRファイルはModuleのディレクトリに置かれ,INFファイル内[Sources]セクションで他のソースと一緒に参照されます。

VFR fileの一例:

#define FORMSET_GUID { 0x9e0c30bc, 0x3f06, 0x4ba6, 0x82, 0x88, 0x9,
0x17, 0x9b, 0x85, 0x5d, 0xbe }
#define FRONT_PAGE_CLASS 0x0000
#define FRONT_PAGE_SUBCLASS 0x0002
#define FRONT_PAGE_FORM_ID 0x1000
#define FRONT_PAGE_ITEM_ONE 0x0001
#define FRONT_PAGE_ITEM_TWO 0x0002
#define FRONT_PAGE_ITEM_THREE 0x0003
#define FRONT_PAGE_ITEM_FOUR 0x0004
#define FRONT_PAGE_ITEM_FIVE 0x0005
#define FRONT_PAGE_KEY_CONTINUE 0x1000
#define FRONT_PAGE_KEY_LANGUAGE 0x1234
#define FRONT_PAGE_KEY_BOOT_MANAGER 0x1064
#define FRONT_PAGE_KEY_DEVICE_MANAGER 0x8567
#define FRONT_PAGE_KEY_BOOT_MAINTAIN 0x9876
#define LABEL_SELECT_LANGUAGE 0x1000
#define LABEL_TIMEOUT 0x2000
#define LABEL_END 0xffff

formset
 guid = FORMSET_GUID,
 title = STRING_TOKEN(STR_FRONT_PAGE_TITLE),
 help = STRING_TOKEN(STR_NULL_STRING),
  classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID,

 form formid = FRONT_PAGE_FORM_ID,
  title = STRING_TOKEN(STR_FRONT_PAGE_TITLE);

  banner
   title = STRING_TOKEN(STR_FRONT_PAGE_COMPUTER_MODEL),
   line 0,
   align left;
 
  banner
   title = STRING_TOKEN(STR_FRONT_PAGE_CPU_MODEL),
   line 1,
   align left; 

  banner
   title = STRING_TOKEN(STR_FRONT_PAGE_CPU_SPEED),
   line 1,
   align right;

  banner
   title = STRING_TOKEN(STR_FRONT_PAGE_BIOS_VERSION),
   line 2,
   align left;

   banner
   title = STRING_TOKEN(STR_FRONT_PAGE_MEMORY_SIZE),
   line 2,
   align right;
 
  goto FRONT_PAGE_ITEM_ONE,
   prompt = STRING_TOKEN(STR_CONTINUE_PROMPT),
   help = STRING_TOKEN(STR_CONTINUE_HELP),
   flags = INTERACTIVE,
   key = FRONT_PAGE_KEY_CONTINUE;
 
  label LABEL_SELECT_LANGUAGE;
  //
  // This is where we will dynamically add a OneOf type op-code to
select
  // Languages from the currently available choices
  //
  label LABEL_END;
 
  goto FRONT_PAGE_ITEM_THREE,
   prompt = STRING_TOKEN(STR_BOOT_MANAGER),
   help = STRING_TOKEN(STR_BOOT_MANAGER_HELP),
   flags = INTERACTIVE,
   key = FRONT_PAGE_KEY_BOOT_MANAGER;

  goto FRONT_PAGE_ITEM_FOUR,
   prompt = STRING_TOKEN(STR_DEVICE_MANAGER),
   help = STRING_TOKEN(STR_DEVICE_MANAGER_HELP),
   flags = INTERACTIVE,
   key = FRONT_PAGE_KEY_DEVICE_MANAGER;

  goto FRONT_PAGE_ITEM_FIVE,
   prompt = STRING_TOKEN(STR_BOOT_MAINT_MANAGER),
   help = STRING_TOKEN(STR_BOOT_MAINT_MANAGER_HELP),
   flags = INTERACTIVE,
   key = FRONT_PAGE_KEY_BOOT_MAINTAIN;

 endform;

endformset; 

【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

【EDK II】EDK IIの基礎

UEFIの学習,EDKIIによるModule開発を目的として,EDK ||に関する基礎知識などをまとめていきます。内容は随時追加,構成は変更される場合があります。
備忘録としてここに書き留めます。(随時更新,まとまったらGitBookなどに移動予定)

Module,Package,そしてPlatform

Module

Moduleは,最小のコンパイル可能なコードやプレビルドされたバイナリです。
Moduleはソースコードかバイナリのメタデータ(INF)ファイルを含んでいます。
INFファイルは,Moduleがどんなライブラリクラス,Ppi
GUIDs,Protocols,Pcdsなどの情報を記述するためにビルドシステムに必要となります。

例えば,$WORKSPACE/MdeModulePkg/Universal/Bus/Pci/UhciDxeが,
上述したソースファイルとINFファイルがModuleを構成します。

INFファイルのシンタックスは「EDK II Extended INF Specification」を参照してください。

Package

Packageは0以上のModuleのグループです。
PackageはDECファイルやDSCファイルを含んでいなければなりません。

機能上は,Packageはプロジェクトを論理分割したものです。
Moduleをどこに置くべきか決めるためには,開発者はライセンスやスペックコンプライアンスなどの合理的な判断に依存します。
これらのメタデータファイルやModuleのINFファイルは,
ビルドシステムがオプションに基づいて自動的にMakefileや単一のModuleチップか全体のflashチップを生成します。
DSC,DECファイルのシンタックスについては「EDK II DSC File Specification」や「EDK || DEC File Specification」を参照してください。

Platform

PlatformはPackageにメタファイルを追加した,Packageの特殊なタイプ(呼称)です。
Packageは1つのDSCファイルと,0か1個のFDFファイルを含んでいる必要があります。Flashのアウトプットが欲しい時だけ,FDFファイルを用意する必要があります。

FDFファイルに関しては「EDK || FDF File Specification」を参照してください。

PlatformをEDKからEDK ||への移植方法については「EDK || Platform Port Guide」を参照ください。

Moduleのカスタマイズ

「EDK II User Manual」を使って,Library class/Library instanceとPCDメカニズムの設計目的を理解してください。これらのメカニズムは開発者に対して,ソースコードを変更せずにモジュールをカスタマイズすることを可能にしています。

Library クラス/インスタンス

パフォーマンズやイメージサイズ,Moduleタイプの制限などの要求次第で,開発者は適切なLibraryインスタンスを選ぶことになります。

$WORKSPACE/MdePkg/のcore Packageには,多くのライブラリクラスとそれに対応したインスタンスがあります。
これらのLibraryクラスによって提供されるヘルパー関数のAPIに関する基礎的な情報は,$WORKSPACE/MdePkg/include/libraryディレクトリを参照してください。

PCD

開発者はModuleの外から情報を展開したり,Moduleの中の手続きをコントロールすることに対して,PCDメカニズムは利点があります。
その情報はDSCやDECファイルをコンパイル際に取得できるかもしれませんが,いくつかのファイルはFlashイメージ作成にできたり,実行時に決定されたりするものです。


EDK II 開発のライフサイクル

EDK IIの開発サイクルは,次の4つのフェーズに分割されます。

Phase 1:Packageの作成

PackageはModuleのコンテナです。
開発者はまず最初に,どこにModuleを置くか考えるべきです。
原則としては,IHV/IBVにより新しく開発されるModuleはEDK II core Packageの中に置くべきではありません(例えばMdePkg,MdeModulePkg,IntelFrameworkPkg,IntelFrameWorkModulePkg)。
その一つの理由は,それらのPackageはModule/Platform開発を促進するベースサポート用に作られたものだからです。これらのPackageはBSD licenseなので開発したModuleをオープンソースにするつもりがないなら,core packageの中に入れるべきではありません。

新たなPackageを作るために,開発者はPackageのインターフェースを定義するためにDECファイルを作成する必要があります。インターフェースには下記を含みます。

・他のPackageからのModuleのためのディレクト
・GUIDの値
・Protocol GUIDの値
PPI GUIDの値
・このPackageによりできるPCDエントリの宣言

Phase 2:メタデータと基本的な機能の実装

Packageの中に置くModuleを決めた後,開発者はModuleの動作を示すINFファイルを作成しなければなりません。それは下記のものを含みます。

・Moduleタイプ
・必要なLibraryクラス
・必要なPPI,PROTOCOL,GUID,PCD
・他のModuleとの依存関係(Moduleタイプによりあったりなかったり)

ModuleのINFファイルを見ることで,あまり知らないModuleについて手軽に概要を知ることができます。
INFファイルを作成後,開発者は基本的な機能を実装すべくソースコードを書き始めましょう。

$WORKSPACE/MdePkg/Include/Libraryディレクトリ以下に,
サポート関数を提供する多くのライブラリがあります。また,種々のModuleタイプのための,エントリポイントのライブラリがあります。詳細はヘッダーファイルに書いてあるので是非参照してください。

Phase 3:ビルドのためのDSCファイルの作成

EDK IIでは,DSCファイルがPackageのビルド動作を記述します。それには下記が含まれます。

・ビルドに必要なModule
・種々のModuleタイプのためのLibraryインスタンスの選択
・Moduleによって使用されるPCDエントリの構成

単一PlatformのDSCや各々参照される別PackageのDECファイルが,Packageを定義します。
Package内のすべてのModuleをビルドするために,これらのファイルやModuleのINFファイルが必要です。

Phase 4:Moduleのチューニング

Moduleをチューニングするために下記のものを使用してください。

・コードをリユースするために,EDK II ライブラリ
・configurationのために,EDK II PCDメカニズム

EDK ModuleとEDK II Moduleの違いは,EDK II Moduleが必要であれば静的にも動的にもカスタマイズ可能であることです。

・Static Customizationは,ライブラリインスタンスを選んだり,ビルド時にPCDのFeatureFlagやFixed Typeを決めることが好まれます。
・Dynamic Customizationは,急いで動作をコントロールするためにパッチ可能/動的なPCDを使うことが好まれます。

EDK II Module開発者は,開発のできるだけ早い段階でどんなロジックがModuleの中に生成されるのか熟慮するべきです。例えば,もしなんらかの機能がcore Packageのライブラリクラス内ですでに実装された場合,開発者はその既存のライブラリクラスを使うべきです。

Build Infrastructure

EDK IIビルドシステムは,クロスプラットフォームでビルドできるようにするため,PythonとPortable Cコードに基づいています。
EDK II ビルドツールがメタデータ(DSC,DEC,INFファイル)を構文解析し,それに対応する一つのTop-level Makefile・Module毎のMakefile・Module毎のAutogen.c,Autogen.hが生成されます。

自動生成されるファイル内で,EDK IIビルドツールはModuleで使用される全てのGUIDs,Protocols,PPIs,PCDsを生成します。そして,Moduleのエントリポイント実装時に,使用されるライブラリインスタンスのすべてのconstructorsを呼びます。

gtk-fortran Vol.2

ボタンの表示

ウィンドウにボタンを表示させるサンプルコードです。

program show_button
  !--------------------------------------------!
  !--- ファイル名は hogehoge.f90 とします。
  !--------------------------------------------!
  use iso_c_binding
  use gtk
  !---
  implicit none
  type(c_ptr) :: window

  !--- GTK+の初期化
  call gtk_init()

  !--- ウィンドゥの設定
  !  第一引数:ウィンドゥの種類
  !    GTK_WINDOW_TOPLEVEL:アプリケーションのメインになるようなウィンドゥ
  !    GTK_WINDOW_POPUP   :クリックした時にポップアップ表示されるようなウィンドゥ
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL)

  !--- タイトルを設定
  call gtk_window_set_title(window, "Hello! World!"//c_null_char)

  !--- ウィンドゥの大きさ設定
  !    第一引数:ウィジェット
  !    第二引数:ウィジェットの幅
  !    第三引数:ウィジェットの高さ
  call gtk_widget_set_size_request(window, 300, 200)

  !--- ボタンウィジェットの作成
  !    第一引数:ボタンのラベル
  button = gtk_button_new_with_label("ボタンテスト"//c_null_char)
  !--- ボタンの配置
  !    第一引数:コンテナウィジェット
  !    第二引数:コンテナ内に配置するウィジェット
  call gtk_container_add(window, button)

  !--- 全てのウィンドゥの表示
  !    第一引数:表示する親ウィジェット
  call gtk_widget_show_all(window)

  !--- メインループ
  call gtk_main()
end program show_button

hogehoge.f90をコンパイルして実行しましょう。

$ gfortran hogehoge.f90 -o hogehoge `pkg-config --cflags --libs gtk-2-fortran` -Wl,-no_pie
$ ./hogehoge


実行すると次のように表示されます。これでボタン表示ができるようになりました。

f:id:convergence_hub:20170115141432p:plain

gtk-fortran Vol.1

私がメインで使用しているfortranの、GUIプログラミングについて備忘録として保存してまいります。

gtk-fortranのインストール

gtk-fortranについて、まずは下記を一読ください。
Home · jerryd/gtk-fortran Wiki · GitHub

以降はmacOSにて開発を進めてまいりますが、推奨環境は Linux です。


macOSにて開発なさる方で導入に苦労されている方がいらっしゃれば、エラー内容と共にコメントください。

gtk-fortran開発者殿はmacOSに詳しくないらしく、一部手動でソースコードを手直し、インストールする必要がございます。)




まずは,単純にウィンドウを表示させるサンプルコードです。

program show_window
  !--------------------------------------------!
  !--- ファイル名は hogehoge.f90 とします。
  !--------------------------------------------!
  use iso_c_binding
  use gtk
  !---
  implicit none
  type(c_ptr) :: window

  !--- GTK+の初期化
  call gtk_init()

  !--- ウィンドゥの設定
  !  第一引数:ウィンドゥの種類
  !    GTK_WINDOW_TOPLEVEL:アプリケーションのメインになるようなウィンドゥ
  !    GTK_WINDOW_POPUP   :クリックした時にポップアップ表示されるようなウィンドゥ
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL)

  !--- タイトルを設定
  call gtk_window_set_title(window, "Hello! World!"//c_null_char)

  !--- ウィンドゥの大きさ設定
  !    第一引数:ウィジェット
  !    第二引数:ウィジェットの幅
  !    第三引数:ウィジェットの高さ
  call gtk_widget_set_size_request(window, 300, 200)

  !--- ウィンドゥの表示
  !    第一引数:表示するウィジェット
  call gtk_widget_show(window)

  !--- メインループ
  call gtk_main()
end program show_window

hogehoge.f90をコンパイルして実行しましょう。

$ gfortran hogehoge.f90 -o hogehoge `pkg-config --cflags --libs gtk-2-fortran` -Wl,-no_pie
$ ./hogehoge


実行すると次のように表示されます。これでwindow表示ができるようになりました。

f:id:convergence_hub:20170115122250p:plain

Windows10設定(sublime text 3,bashrc,mintty)

今回は,Sublime Text 3,bash,minttyの簡単な設定を記します.


 ① テキストエディタをインストール
 ② テキストエディタを用いて設定ファイル
   (.bashrc,.bash_profile,.minttyrc)を作成・編集する

以上を行うことが目的です.



Sublime Text 3のダウンロードとインストール


Sublime Text 3はテキストエディタで使いやすいので今はこれを使っています.
なお,OS XのほうではEmacsを使っていますがそちらもこれに変えるかもしれません.

下記サイトより日本語化までできますので,詳細説明は譲ります.
『初心者向け』 Sublime Text 3 導入と日本語化について - NAVER まとめ

上記にしたがってエディタをインストールしたのち,最低限次のパッケージがあるといいでしょう.

 ・Japanize
  ...日本語化パッケージ
 ・ConvertToUTF8
  ...文字コードUTF-8に変換してくれるパッケージ

その他のテーマなど,詳しい設定をする機会は改めて設けます.


Sublime Text 3をMinttyから起動する


Sublime Text 3の実行ファイルが下記にあるとして説明を続けます.

 C:\Program Files\Sublime Text 3\sublime_text.exe

Minttyを起動後,ホームディレクトリにcommand用のディレクトリを作成します.
このディレクトリにSublime Text 3を起動するコマンド subl を作ることにします.

 $ mkdir command

まずは,普通にSublime Text3を起動し,sublという名前のファイルを新規作成します.
ファイルの内容は次のようにして,commandディレクトリの中に置きます.

#! /bin/sh

/c/Program\ Files/Sublime\ Text\ 3/sublime_text $1 &

これで,sublと打てばテキストエディタが起動しますが,現時点ではそれはこのディレクトリ内にいるときだけです.

したがって,次はどこでこのコマンドを打っても起動するようにPATHを通します.

そのために,ホームディレクトリに.bashrc,.bash_profileという設定ファイルを作成します,

.bashrcの作成


Mintty起動後,次のように.bashrcを作成します.

 $ ~/command/subl ~/.bashrc

.bashrcの中身は次のようにします.

#--- PS1
export PS1="\033[32m\]\u@\h \[\033[33m\W\033[0m\] $ "
#--- PATH4command
export PATH=$HOME/command:$PATH

#--- alias
alias cdd='cd $HOME/Desktop'
alias ls='ls --show-control-chars --color=auto' 


上記ファイルを読み込むことでsublコマンドが使えるようになります.ついでにaliasの設定をしています.

 $ source ~/.bashrc

毎回読み込むためのコマンドを打つのは面倒なので,Mintty起動時に読み込んでくれるように,つぎは.bash_profileを作成します.


.bash_profileの作成

ホームディレクトリ以下に作成します.

 $ subl ~/.bash_profile


中身は次のとおりです.

# .bash_profile
# source .bashrc
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

これでMintty初回起動時には設定ファイルが読み込まれるようになります.

最後にMinttyの設定をして終わります.


.minttyrcの作成

ホームディレクトリに作成します.

 $ subl ~/.minttyrc

私の場合は中身は次のように設定しました.

BoldAsFont=yes
Locale=ja_JP
Columns=90
Rows=30
Font=Consolas
FontHeight=10
BoldAsColour=yes
Transparency=high
OpaqueWhenFocused=no
CursorType=block
CursorColour=255,255,0
IMECursorColour=255,0,0
ForegroundColour=243,243,243
Term=xterm-256color
BackgroundColour=0,0,0