UEFIアプリでファイルを任意のメモリアドレスにロードする方法メモ
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)を読み込み,出力できていることが確認できます.