edk2 ファイル入出力 – その2

前回、edk2 のファイル入出力をやったがちょっと見づらいので書き込みと読み込みを別関数に切り出すことにした。

Hello4Pkg という別のパッケージにしてやってみる。

ファイル構成

  • edk2
    • Hello4Pkg フォルダ
      • Hello4.c
      • Hello4.inf
      • Hello4Pkg.dec
      • Hello4Pkg.dsc
  • disk.img

Hello4.c はこんな感じ。ハイライト行が切り出した「書き込み」、「読み込み」処理。

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/MemoryAllocationLib.h>
#include <Guid/FileInfo.h>
#include <Guid/FileSystemInfo.h>

EFI_STATUS
WriteFile(
    IN EFI_FILE_PROTOCOL *Root,
    IN CHAR16 *FileName,
    IN CHAR16 *Data
)
{
    EFI_STATUS Status;
    EFI_FILE_PROTOCOL *File;
    UINTN WriteSize = StrLen(Data) * sizeof(CHAR16);

    Status = Root->Open(
        Root,
        &File,
        FileName,
        EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ,
        0
    );
    if (EFI_ERROR(Status)) {
        Print(L"fail: to create file\n");
        return Status;
    }

    Status = File->Write(File, &WriteSize, Data);
    if (EFI_ERROR(Status)) {
        Print(L"fail: to write file\n");
        File->Close(File);
        return Status;
    }
    
    File->Close(File);
    return EFI_SUCCESS;
}

EFI_STATUS
ReadFile(
    IN EFI_FILE_PROTOCOL *Root,
    IN CHAR16 *FileName,
    OUT CHAR16 *Buffer,
    IN UINTN BufferSize,
    OUT UINTN *ReadSize
)
{
    EFI_STATUS Status;
    EFI_FILE_PROTOCOL *File;

    Status = Root->Open(
        Root,
        &File,
        FileName,
        EFI_FILE_MODE_READ,
        0
    );
    if (EFI_ERROR(Status)) {
        Print(L"fail: to open file for reading\n");
        return Status;
    }

    *ReadSize = BufferSize;
    Status = File->Read(File, ReadSize, Buffer);
    if (EFI_ERROR(Status)) {
        Print(L"fail: to read file: %r\n", Status);
        File->Close(File);
        return Status;
    }

    File->Close(File);
    return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
UefiMain(
    IN EFI_HANDLE ImageHandle,
    IN EFI_SYSTEM_TABLE *SystemTable
)
{
    EFI_STATUS Status;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
    EFI_FILE_PROTOCOL *Root;
    CHAR16 *FileName = L"\\test.txt";

    // Load Protocol
    Status = SystemTable->BootServices->LocateProtocol(
        &gEfiSimpleFileSystemProtocolGuid,
        NULL,
        (VOID**)&FileSystem
    );
    if (EFI_ERROR(Status)) {
        Print(L"faile: locate file system protocol\n");
        return Status;
    }

    Status = FileSystem->OpenVolume(FileSystem, &Root);
    if (EFI_ERROR(Status)) {
        Print(L"faile: open root directory\n");
        return Status;
    }

    CHAR16 *WriteData = L"Hello file system\n";

    Status = WriteFile(Root, FileName, WriteData);
    if (EFI_ERROR(Status)) {
        Root->Close(Root);
        return Status;
    }

    // read
    CHAR16 buf[128];
    UINTN ReadSize = sizeof(buf);
    Status = ReadFile(Root, FileName, buf, ReadSize, &ReadSize);
    if (EFI_ERROR(Status)) {
        Root->Close(Root);
        return Status;
    }

    Print(L"Read from file: %s\n",  buf);

    Root->Close(Root);

    return EFI_SUCCESS;
}

Hello4Pkg.dec ファイルはほとんど変わらない。PACKAGE_NAME は何でもいいのですが、混乱しないように Hello4Pkg に揃えておきます。

[Defines]
DEC_SPECIFICATION   = 0x00010005
PACKAGE_NAME        = Hello4Pkg
PACKAGE_GUID        = 08F29A4C-BB2E-4417-B8E9-3854083B9CC0
PACKAGE_VERSION     = 0.0.1

[Includes]

[Guids]

[Protocols]

[PcdsFixedAtBuild]

Hello4Pkg.dsc も Hello4Pkg に変える以外は変更なし。

[Defines]
PLATFORM_NAME           = Hello4Pkg
PLATFORM_GUID           = 43B8BF63-3328-4A67-83C5-57EA4FAF43DA
PLATFORM_VERSION        = 0.0.1
DSC_SPECIFICATION       = 0x00010005
OUTPUT_DIRECTORY        = Build/Hello4Pkg
SUPPORTED_ARCHITECTURES = X64
BUILD_TARGETS           = DEBUG | RELEASE | NOOPT
SKUID_IDENTIFIER        = DEFAULT

[LibraryClasses]
UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf


BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf
UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf

[Components]
Hello4Pkg/Hello4.inf

最後にビルドと実行

$ cd edk2
$ build -p Hello4Pkg/Hello4Pkg.dsc
$ cd ..

# イメージにファイルコピー
$ sudo mount -o loop,uid=$(id -u),gid=$(id -u) disk.img ./mnt
$ cp ./edk2/Build/Hello4Pkg/DEBUG_CLANGDWARF/X64/Hello4.efi ./mnt/EFI/BOOT
$ sudo umount ./mnt

# 実行
$ qemu-system-x86_64 \
		-drive if=pflash,format=raw,readonly=on,file=edk2/Build/OvmfX64/DEBUG_GCC/FV/OVMF_CODE.fd \
		-drive if=pflash,format=raw,file=edk2/Build/OvmfX64/DEBUG_GCC/FV/OVMF_VARS.fd \
		-drive format=raw,file=disk.img

# qemu で自動起動しないとき
> fs0:\EFI\BOOT\hello4.efi

それにしても、hello4.efi を BOOTX64.efi というファイル名にリネームしても自動起動しないのはなぜなんだろう。まだまだ謎は多い。

2025-2-12 追記:
> BOOTX64.EFI が正解。イメージディスクは FAT32 なので一度作成してしまったあとで UEFIシェルでは、BOOTX64.efi からBOOTX.EFI にリネームできない。(FAT の仕様では、ファイル名の大文字/小文字の区別はない)

次はメモリマップの作成にかかる。カーネルを読み込むまでにはまだ道のりが遠い。