UEFI ファームウェアの開発プロセスにおいて、コンポーネント内部からシリアルコンソール上で利用可能なシェル機能を実行したい場合があります。これは Linux 環境における C プログラム内の system() 関数呼び出しと機能的には類似していますが、UEFI 標準プロトコルを活用する必要があります。
モジュール定義ファイル(INF)の設定
まず、プロジェクト用の INF ファイルを作成します。ここではモジュールタイプとして UEFI_APPLICATION を指定し、必要となるライブラリとプロトコルを依存関係として記述します。
[Defines]
INF_VERSION = 0x0001001A
BASE_NAME = Edk2ShellCaller
FILE_GUID = 12345678-ABCD-EF01-2345-6789ABCDEF01
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.1
ENTRY_POINT = Edk2Entry
[Sources]
Edk2ShellCaller.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
UefiBootServicesTableLib
[Protocols]
gEfiShellProtocolGuid
ソースコードの実装
メインのエントリーポイントでは、ブートサービステーブルを使用してシェンプラットフォームのプロトコルを検索します。検索が成功した場合、取得したプロトコルポインタを通じてコマンドを実行します。
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/Shell.h>
#define DEFAULT_STATUS EFI_SUCCESS
/**
* UEFI アプリケーションのエントリポイント。
* @param ImageHandle 画像ハンドル
* @param SystemTable システムテーブル
*/
EFI_STATUS EFIAPI Edk2Entry(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS RetCode = DEFAULT_STATUS;
EFI_SHELL_PROTOCOL *pShellInterface = NULL;
// 監視タイマーのリセット
SystemTable->BootServices->SetWatchdogTimer(0, 0, 0, NULL);
Print(L"Starting Shell Command Execution...\r\n");
// UEFI シェルプロトコルの検索
RetCode = SystemTable->BootServices->LocateProtocol(
&gEfiShellProtocolGuid,
NULL,
(VOID **)&pShellInterface
);
if (EFI_ERROR(RetCode)) {
Print(L"Failed to locate shell protocol.\r\n");
return RetCode;
}
// シェルコマンド「version」の実行
RetCode = pShellInterface->Execute(
&ImageHandle,
L"version",
NULL,
&RetCode
);
if (!EFI_ERROR(RetCode)) {
Print(L"Command executed successfully.\r\n");
} else {
Print(L"Command execution failed.\r\n");
}
return RetCode;
}
ビルド構成への組み込み
最後に、ターゲット DSC ファイル(例:MdeModulePkg.dsc)の [Components] セクションへ新しく作成した INF ファイルを登録します。
[Components.Ia32]
# ... 既存のコンポーネント ...
Edk2App/Edk2ShellCaller.inf
上記の手順完了後、EDK2 ビルドツールチェーンを使用してバイナリを生成すれば、アプリケーション起動時に指定された UEFI シェル機能が実行されます。ビルド環境の構築については、各 EDK2 バージョンに対応した環境構築ガイドを参照してください。