UEFIアプリケーションでファイルを任意のメモリアドレスにロードする方法についてのメモです. ブートローダーを作ったりするのに役立つと思います.
開発環境はEDK2(28f27af6f007c3794fcc9d098ef91713160f4e5b
),OSはArch Linuxを使いました.
これを行うにあたって,そもそもUEFIって何? 環境構築の方法は? 仕様書の読み方は? プロトコルとハンドラって何? どうやって動作確認するの?...等々いろいろわからないことがあり調べたのですが,今回はメモなので記述を省略します. 気力があれば後日書きます.
まだまだ勉強途中なので,間違い等ありましたらコメントでの指摘をお願いします.
ファイルを任意メモリアドレスにロードする方法
ソースコード
コードはAppPkg/Application/Helloを改変して作りました.
まずはiniファイルを示します.
## @file # A simple, basic, EDK II native, "hello" application. # # Copyright (c) 2010, 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. # # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. # ## [Defines] INF_VERSION = 0x00010006 BASE_NAME = Hello FILE_GUID = a912f198-7f0e-4803-b908-b757b806ec83 MODULE_TYPE = UEFI_APPLICATION VERSION_STRING = 0.1 ENTRY_POINT = UefiMain # # VALID_ARCHITECTURES = IA32 X64 IPF # [Sources] Hello.c [Packages] MdePkg/MdePkg.dec ShellPkg/ShellPkg.dec [LibraryClasses] UefiApplicationEntryPoint UefiLib [Guids] gEfiFileInfoGuid [Protocols] gEfiSimpleFileSystemProtocolGuid
次にCのソースを示します.
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Library/DebugLib.h> #include <Library/MemoryAllocationLib.h> #include <Protocol/DevicePath.h> #include <Protocol/GraphicsOutput.h> #include <Protocol/SimpleFileSystem.h> #define BUF_MAX_SIZE (512*1024) // 512KB #define KERNEL_BASE 0x200000 EFI_SYSTEM_TABLE *gST; EFI_BOOT_SERVICES *gBS; EFI_STATUS EFIAPI UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { Print(L"Hello there fellow Programmer.\n"); Print(L"Welcome to the world of EDK II.\n"); EFI_STATUS Status = EFI_SUCCESS; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFile; EFI_FILE_PROTOCOL *Root; EFI_FILE_PROTOCOL *File; CHAR16 *Path = L"hoge.txt"; gST = SystemTable; gBS = gST->BootServices; Status = gBS->LocateProtocol ( &gEfiSimpleFileSystemProtocolGuid, NULL, (VOID **)&SimpleFile ); if (EFI_ERROR (Status)) { Print(L"%r on Locate EFI Simple File System Protocol.\n", Status); return Status; } Status = SimpleFile->OpenVolume (SimpleFile, &Root); if (EFI_ERROR (Status)) { Print(L"%r on Open volume.\n", Status); return Status; } Status = Root->Open (Root, &File, Path, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY); if (EFI_ERROR (Status)) { Print(L"%r on Open file.\n", Status); return Status; } UINTN BufferSize = 0; EFI_PHYSICAL_ADDRESS Buffer; BufferSize = BUF_MAX_SIZE; Buffer = (EFI_PHYSICAL_ADDRESS)KERNEL_BASE; // if you want to know MemoryType List, please refer UEFI Spec2.5 document pp.152-153 Status = gBS->AllocatePages( AllocateAddress, EfiLoaderData, BufferSize / 4 / 1024, // 1page = 4KiB &Buffer ); if (EFI_ERROR (Status)) { Print(L"%Could not allocate memory pool %r\n", Status); return Status; } Status = File->Read( File, &BufferSize, (VOID *)Buffer ); if (EFI_ERROR (Status)) { Print(L"%r on Open file.\n", Status); return Status; } Print(L"BufferSize = %d\n", BufferSize); Print(L"Address = %p\n", Buffer); for (UINTN i = 0; i < BufferSize; i++) { Print(L"%c", ((CHAR8 *)Buffer)[i]); } Print(L"\n"); File->Close(File); Root->Close(Root); gBS->FreePages(Buffer, BUF_MAX_SIZE); Print(L"All success hoge.txt\n"); return EFI_SUCCESS; }
処理の流れ
このコードの流れを日本語で示すと,次のようになっています.
- BootService(以降BS)からLocateProtocol関数を呼び出し,ファイルアクセスするためのプロトコル,EFI_SIMPLE_FILE_SYSTEM_Protocol(以降SimpleFile)を取得する
- SimpleFileのOpenVolume関数でデバイスのルートディレクトリを開き,ルートディレクトリのファイルハンドラ(Root)を取得する
- RootのOpen関数で指定したファイル(今回はhoge.txt)を開き,そのファイルハンドラ(File)を取得する
- BSからAllocatePages関数を呼び出し,任意アドレスからメモリを確保する
- ファイルハンドラFileのRead関数を呼び出し,ファイルの内容を確保したメモリ領域に読みこむ
- ファイルハンドラを閉じる
- メモリを開放する
ファイルを場所を気にせず読みこむだけであれば,AllocatePool関数を使うことで手軽にメモリを確保できます. しかし,今回は「任意」のメモリアドレスにファイルを読み込むため,AllocatePagesという関数を用いています.
AllocatePages関数は第一引数にAllocateAddressを指定することで,第4引数で指摘したアドレスから第3引数で指定したページ分(1ページは4KiB)メモリを確保しようと試みます.
メモリが確保できない場合は,NOT FOUNDエラーが返ります.どの領域でどの程度メモリが取れるのかは,調べているところなのでまだわかっていません.
実行結果
メモリの確保が行われ,正常に動作した場合のログは以下のようになります.
UEFI Interactive Shell v2.1 EDK II UEFI v2.50 (EDK II, 0x00010000) Mapping table FS0: Alias(s):HD7a1:;BLK3: PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1) BLK2: Alias(s): PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0) BLK4: Alias(s): PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0) BLK0: Alias(s): PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0) BLK1: Alias(s): PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x1) Press ESC in 5 seconds to skip startup.nsh or any other key to continue. Shell> fs0: FS0:\> Hello.efi Hello there fellow Programmer. Welcome to the world of EDK II. BufferSize = 5 Address = 200000 hoge
コードで指定したKERNEL_BASEのアドレス0x200000からメモリを取得し,hoge.txtの内容(hoge)を読み込み,出力できていることが確認できます.