3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BIOSは死んだ、UEFIの時代だ Part3

Last updated at Posted at 2025-12-10

UEFI完全入門シリーズ
Part1【環境構築】 | Part2【GOP】 | Part3【ファイル読み込み】| Part4【メモリマップ】| Part5【カーネルロード】

はじめに

前回はGOPでグラフィックスを描画しました。

今回は Simple File System Protocol を使って、ディスクからファイルを読み込みます。

ブートローダーの最も重要な仕事は「カーネルをロードすること」。そのためにはファイルシステムへのアクセスが必須です。

Simple File System Protocol

UEFIのファイルシステムアクセスは2つのプロトコルで構成されています:

  1. EFI_SIMPLE_FILE_SYSTEM_PROTOCOL - ボリュームを開く
  2. EFI_FILE_PROTOCOL - ファイル/ディレクトリ操作

アクセスの流れ

ImageHandle
    ↓ HandleProtocol (LOADED_IMAGE_PROTOCOL_GUID)
LoadedImage->DeviceHandle
    ↓ HandleProtocol (SIMPLE_FILE_SYSTEM_PROTOCOL_GUID)
FileSystem
    ↓ OpenVolume()
Root (EFI_FILE_PROTOCOL)
    ↓ Open()
File (EFI_FILE_PROTOCOL)

実装

必要なGUID

EFI_GUID LoadedImageProtocolGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
EFI_GUID SimpleFileSystemProtocolGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
EFI_GUID FileInfoGuid = EFI_FILE_INFO_ID;

ファイルシステムを取得

// 1. LoadedImage Protocolを取得
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
Status = uefi_call_wrapper(
    BS->HandleProtocol, 3,
    ImageHandle,
    &LoadedImageProtocolGuid,
    (VOID **)&LoadedImage
);

// 2. ファイルシステムプロトコルを取得
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
Status = uefi_call_wrapper(
    BS->HandleProtocol, 3,
    LoadedImage->DeviceHandle,
    &SimpleFileSystemProtocolGuid,
    (VOID **)&FileSystem
);

// 3. ルートディレクトリを開く
EFI_FILE_PROTOCOL *Root;
Status = uefi_call_wrapper(
    FileSystem->OpenVolume, 2,
    FileSystem,
    &Root
);

LoadedImage->DeviceHandle は、このブートローダーが起動されたデバイス(ESPがあるディスク)を指します。

実行結果

=== Simple File System Protocol Demo ===

LoadedImage obtained
  ImageBase: 0x6155000
  ImageSize: 69632 bytes

FileSystem protocol obtained

Root directory opened

Files in root directory:
  [DIR]  EFI
  [FILE] NvVars (11741 bytes)

ImageBaseImageSizeは、現在実行中のUEFIアプリケーション自身のメモリ上の位置とサイズです。

ディレクトリの読み取り

EFI_FILE_PROTOCOL->Read() でディレクトリの内容を列挙できます。

UINT8 Buffer[512];
UINTN BufferSize;

while (1) {
    BufferSize = sizeof(Buffer);
    Status = uefi_call_wrapper(
        Root->Read, 3,
        Root,
        &BufferSize,
        Buffer
    );
    
    if (EFI_ERROR(Status) || BufferSize == 0) {
        break;  // エントリがなくなった
    }
    
    EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)Buffer;
    
    if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
        Print(L"  [DIR]  %s\n", FileInfo->FileName);
    } else {
        Print(L"  [FILE] %s (%d bytes)\n", 
              FileInfo->FileName, 
              FileInfo->FileSize);
    }
}

EFI_FILE_INFO構造体

typedef struct {
    UINT64 Size;           // この構造体のサイズ
    UINT64 FileSize;       // ファイルサイズ
    UINT64 PhysicalSize;   // 物理サイズ(クラスタ境界)
    EFI_TIME CreateTime;   // 作成日時
    EFI_TIME LastAccessTime;
    EFI_TIME ModificationTime;
    UINT64 Attribute;      // ファイル属性
    CHAR16 FileName[];     // ファイル名(可変長)
} EFI_FILE_INFO;

ファイルの作成と書き込み

ファイルを作成

EFI_FILE_PROTOCOL *File;

Status = uefi_call_wrapper(
    Root->Open, 5,
    Root,
    &File,
    L"test.txt",
    EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,
    0  // 属性(0 = 通常ファイル)
);

ファイルモード

フラグ 説明
EFI_FILE_MODE_READ 0x01 読み取り
EFI_FILE_MODE_WRITE 0x02 書き込み
EFI_FILE_MODE_CREATE 0x8000000000000000 存在しなければ作成

ファイルに書き込み

CHAR8 TestData[] = "Hello from UEFI!\nThis is a test file.\n";
UINTN DataSize = sizeof(TestData) - 1;  // NULL終端を除く

Status = uefi_call_wrapper(
    File->Write, 3,
    File,
    &DataSize,
    TestData
);

Print(L"Written %d bytes to test.txt\n", DataSize);

// ファイルを閉じる
uefi_call_wrapper(File->Close, 1, File);

実行結果:

--- Creating test file ---
Written 38 bytes to test.txt

ファイルの読み込み

ファイルを開く

Status = uefi_call_wrapper(
    Root->Open, 5,
    Root,
    &File,
    L"test.txt",
    EFI_FILE_MODE_READ,
    0
);

ファイルサイズを取得

EFI_FILE_INFO *Info;
UINT8 InfoBuffer[256];
UINTN InfoSize = sizeof(InfoBuffer);

Status = uefi_call_wrapper(
    File->GetInfo, 4,
    File,
    &FileInfoGuid,
    &InfoSize,
    InfoBuffer
);

Info = (EFI_FILE_INFO *)InfoBuffer;
Print(L"File size: %d bytes\n", Info->FileSize);

ファイル内容を読み込み

CHAR8 ReadBuffer[256];
UINTN ReadSize = sizeof(ReadBuffer) - 1;

Status = uefi_call_wrapper(
    File->Read, 3,
    File,
    &ReadSize,
    ReadBuffer
);

ReadBuffer[ReadSize] = '\0';
Print(L"Content: %a\n", ReadBuffer);  // %a は ASCII文字列用

uefi_call_wrapper(File->Close, 1, File);

実行結果:

--- Reading test file ---
File size: 38 bytes
Content: Hello from UEFI!
This is a test file.

完全なサンプルコード

#include <efi.h>
#include <efilib.h>

EFI_GUID LoadedImageProtocolGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
EFI_GUID SimpleFileSystemProtocolGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
EFI_GUID FileInfoGuid = EFI_FILE_INFO_ID;

EFI_STATUS
EFIAPI
efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_STATUS Status;
    EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
    EFI_FILE_PROTOCOL *Root;
    EFI_FILE_PROTOCOL *File;
    
    InitializeLib(ImageHandle, SystemTable);
    ST->ConOut->ClearScreen(ST->ConOut);
    
    Print(L"=== Simple File System Protocol Demo ===\n\n");
    
    // LoadedImage Protocol
    Status = uefi_call_wrapper(
        BS->HandleProtocol, 3,
        ImageHandle, &LoadedImageProtocolGuid, (VOID **)&LoadedImage
    );
    if (EFI_ERROR(Status)) {
        Print(L"ERROR: Failed to get LoadedImage: %r\n", Status);
        goto done;
    }
    
    Print(L"LoadedImage obtained\n");
    Print(L"  ImageBase: 0x%lx\n", LoadedImage->ImageBase);
    Print(L"  ImageSize: %d bytes\n\n", LoadedImage->ImageSize);
    
    // FileSystem Protocol
    Status = uefi_call_wrapper(
        BS->HandleProtocol, 3,
        LoadedImage->DeviceHandle,
        &SimpleFileSystemProtocolGuid, (VOID **)&FileSystem
    );
    if (EFI_ERROR(Status)) {
        Print(L"ERROR: Failed to get FileSystem: %r\n", Status);
        goto done;
    }
    
    // Root Directory
    Status = uefi_call_wrapper(
        FileSystem->OpenVolume, 2, FileSystem, &Root
    );
    if (EFI_ERROR(Status)) {
        Print(L"ERROR: Failed to open root: %r\n", Status);
        goto done;
    }
    
    // ディレクトリを列挙
    Print(L"Files in root directory:\n");
    UINT8 Buffer[512];
    UINTN BufferSize;
    
    while (1) {
        BufferSize = sizeof(Buffer);
        Status = uefi_call_wrapper(Root->Read, 3, Root, &BufferSize, Buffer);
        if (EFI_ERROR(Status) || BufferSize == 0) break;
        
        EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)Buffer;
        if (FileInfo->Attribute & EFI_FILE_DIRECTORY) {
            Print(L"  [DIR]  %s\n", FileInfo->FileName);
        } else {
            Print(L"  [FILE] %s (%d bytes)\n", 
                  FileInfo->FileName, FileInfo->FileSize);
        }
    }
    
    // ファイルを作成して書き込み
    uefi_call_wrapper(Root->Close, 1, Root);
    uefi_call_wrapper(FileSystem->OpenVolume, 2, FileSystem, &Root);
    
    Print(L"\n--- Creating test file ---\n");
    Status = uefi_call_wrapper(
        Root->Open, 5, Root, &File, L"test.txt",
        EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0
    );
    
    if (!EFI_ERROR(Status)) {
        CHAR8 TestData[] = "Hello from UEFI!\nThis is a test file.\n";
        UINTN DataSize = sizeof(TestData) - 1;
        uefi_call_wrapper(File->Write, 3, File, &DataSize, TestData);
        Print(L"Written %d bytes to test.txt\n", DataSize);
        uefi_call_wrapper(File->Close, 1, File);
    }
    
    // ファイルを読み込み
    Print(L"\n--- Reading test file ---\n");
    Status = uefi_call_wrapper(
        Root->Open, 5, Root, &File, L"test.txt", EFI_FILE_MODE_READ, 0
    );
    
    if (!EFI_ERROR(Status)) {
        CHAR8 ReadBuffer[256];
        UINTN ReadSize = sizeof(ReadBuffer) - 1;
        uefi_call_wrapper(File->Read, 3, File, &ReadSize, ReadBuffer);
        ReadBuffer[ReadSize] = '\0';
        Print(L"Content: %a\n", ReadBuffer);
        uefi_call_wrapper(File->Close, 1, File);
    }
    
    uefi_call_wrapper(Root->Close, 1, Root);
    
done:
    Print(L"\nPress any key to exit...\n");
    UINTN Index;
    uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &Index);
    return EFI_SUCCESS;
}

EFI_FILE_PROTOCOLの主要関数

関数 説明
Open ファイル/ディレクトリを開く
Close ファイルを閉じる
Delete ファイルを削除
Read 読み込み
Write 書き込み
GetPosition 読み書き位置を取得
SetPosition 読み書き位置を設定
GetInfo ファイル情報を取得
SetInfo ファイル情報を設定
Flush バッファをフラッシュ

ファイルパスについて

UEFIでは、パス区切りにバックスラッシュ \ を使用します(Windowsと同じ)。

// サブディレクトリのファイルを開く
Root->Open(Root, &File, L"\\EFI\\BOOT\\config.txt", EFI_FILE_MODE_READ, 0);

ルートからの相対パスとして指定します。

まとめ

今回学んだこと:

  1. Simple File System Protocol でボリュームにアクセス
  2. EFI_FILE_PROTOCOL でファイル/ディレクトリを操作
  3. ディレクトリの列挙 - Read()EFI_FILE_INFO を取得
  4. ファイルの作成と書き込み - Open() + Write()
  5. ファイルの読み込み - Open() + Read()

次回はメモリマップを取得して、カーネルをロードするためのメモリ管理を学びます。ExitBootServices()を呼んでOSに制御を移すまであと少し!

参考資料

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?