UEFIアプリケーションのはじめかた

UEFI Advent Calender 12日目だったoruminです
盛大に遅刻かましてもはや遅刻とさえ言えない感じです。

UEFIのアプリケーション、まずどう作るかわからない方も多いかと思います。

まず、UEFIが実行できる実行形式について、これはWindowsと同じPEバイナリです。

また、EDKのようなツールキットを用いる必要がありますが、
今回はgnu-efiで説明します。

gnu-efiとは、BSDLなEFIアプリケーション開発用ライブラリです。
LinuxやBSDでの開発に親和性が高いと思われます。

これは最近だとaptやpacmanでそのままバイナリインストールが可能です。

そして、次にこのようなファイルを用意します

#include<efi.h>
#include<efilib.h>
EFI_STATUS
efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
InitializeLib(ImageHandle, SystemTable);
Print(L"Hello, EFI!\n");
return EFI_SUCCESS;
}
view raw hello.c hosted with ❤ by GitHub

ARCH = x86_64
EFIROOT = /usr
HDDRROOT = $(EFIROOT)/include/efi
INCLUDES = -I. -I$(HDDRROOT) -I$(HDDRROOT)/$(ARCH) -I$(HDDRROOT)/protocol
CRTOBJS = $(EFIROOT)/lib/crt0-efi-$(ARCH).o
CFLAGS = -O2 -fPIC -Wall -fshort-wchar -fno-strict-aliasing -fno-merge-constants -mno-red-zone
ifeq ($(ARCH),x86_64)
CFLAGS += -DEFI_FUNCTION_WRAPPER
endif
CPPFLAGS = -DCONFIG_$(ARCH)
FORMAT = efi-app-$(ARCH)
INSTALL = install
LDFLAGS = -nostdlib
LDSCRIPT = $(EFIROOT)/lib/elf_$(ARCH)_efi.lds
LDFLAGS += -T $(LDSCRIPT) -shared -Bsymbolic -L$(EFIROOT)/lib $(CRTOBJS)
LOADLIBS = -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name)
prefix =
CC = $(prefix)gcc
AS = $(prefix)as
LD = $(prefix)ld
AR = $(prefix)ar
RANLIB = $(prefix)ranlib
OBJCOPY = $(prefix)objcopy
%.efi: %.so
$(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \
-j .rela -j .reloc --target=$(FORMAT) $*.so $@
%.so: %.o
$(LD) $(LDFLAGS) $^ -o $@ $(LOADLIBS)
%.o: %.c
$(CC) $(INCLUDES) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
TARGETS = hello.efi
all: $(TARGETS)
clean:
rm -f $(TARGETS)
view raw Makefile hosted with ❤ by GitHub


前者がUEFIでのHello, World!です。efi_mainがエントリポイント、
ImageHandleもSystemTableもUEFIのAPI(Protocol)を呼び出し、使うのに必要なパラメータとなります。
今回は、gnu-efiの初期化をしてから文字列を出力するだけなので特に使用していません。
デバッグ文字列出力は
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello, EFI!\n");
でも良いのですが。

後者のMakefileについては、書いてある通りです。
ライブラリとインクルードファイルの指定、
それにリンカスクリプトやスタートアップルーチンはgnu-efiのものを使うように指定。
shared objectな実行ファイルを作ってから、
objcopyコマンドで必要な部分を抜き出す(ELFヘッダは要らないので)
という流れになります。
CFLAGSについては、色々ごちゃっと指定してますが、
まず必須なのは-fPICと-fshort-wchar、そしてx86_64であれば-DEFI_FUNCTION_WRAPPERだけで、あとのは特段必ず指定しないといけないものではないです。

さて、これでmakeをしますと、hello.efiが作成されるので、

あとはEFIシステムパーティション直下にでも置いてあげて、起動時にUEFI Shellを立ち上げてから
hello.efi
とコマンドを入力してみてください。Hello, EFI!と文字列が出たら成功です!

UEFIの開発やProtocolについては、1日目の記事の最後に書いたドキュメントがオススメですが、
リファレンスよりも手軽にProtocolの型や名前だけ把握するには、
Phoenixのwikiが便利です。