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

Converg.

ものづくり関係の備忘録

【EDK II】Module開発

EDK II UEFI 自作OS

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

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;