NO_WAIT

主にプログラミング

gnu-efiのEFIサンプルアプリケーションをQEMU上で動作させる

(U)EFIアプリケーションを作成し試走させる実験環境が欲しくなりました。実機よりエミュレータなどのほうが手軽で望ましいと思っていたところ、QEMU向けUEFIファームウェアがすでにあったので実際に動かしてみました。以下、そのときの手順を備忘録としてまとめました。 注意:本記事に記載されている手順は安全ではない可能性があります。

実験環境

  • ハードウェア: Intel NUC D54250WYKH
  • OS: ubuntu 14.04 LTS
  • kernel release (uname -r): 3.13.0-24-generic
  • kernel version (uname -v): #47-Ubuntu SMP Fri May 2 23:31:42 UTC 2014
  • machine (uname -m): i686
  • make: GNU Make 3.81
  • gcc: version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
  • binutils: GNU Binutils for Ubuntu 2.24
  • gnu-efi-3.0

実験

gnu-efiのビルドからQEMU上でのアプリケーション実行までの手順は UEFI - OSDev Wiki に詳述されています。 今回の環境ではクロスコンパイラは必要なかったので意外と簡単に進めることが出来ました。

gnu-efiの取得

sourceforgeからgnu-efi_3.0v.orig.tar.gzをダウンロードします。 以下、ディレクトリ$HOME/gnu-efiを作成し、その下で作業します。

$ cd $HOME/gnu-efi
$ tar zxvf gnu-efi_3.0v.orig.tar.gz 

Makefileの編集

gnu-efiはconfigureスクリプトを実行せず、直接make, make installでインストールできますが、Makefileの共通設定が記述されたMake.defaultsファイルを環境に合わせて修正します。

INSTALLROOT

INSTALLROOTのデフォルト値は/になっていますが、システムを汚さずホームディレクトリ以下で作業したいので$(HOME)/gnu-efiに変更します。

PREFIXのデフォルトは/usr/localですがこれはそのままにしておきます。

$INSTALLROOT/$PREFIX/libにライブラリが、$INSTALLROOT/$PREFIX/incにヘッダファイルがインストールされます。ここでは$HOME/gnu-efi/usr/localというディレクトリが作成されることになります。

prefix

prefixはデフォルトの/usr/binのままとしますが、環境によっては調整が必要な場合もあります。

ビルド

gnu-efi-3.0のディレクトリでmakeするだけでライブラリがビルドされます。make installINSTALLROOT下にインストールされます。

サンプルUEFIアプリケーションがappsディレクトリにあります。これも同様にmakeすると*.efiというファイルができます。これがアプリケーションの実行形式で実体はPEファイルです。

$ cd $HOME/gnu-efi/gnu-efi-3.0
$ make
$ make install
$ cd apps
$ make

EFIファームウェアの取得

SourceForge.net: OVMF - tianocore:からOVMFをダウンロードします。ダウンロードしたzipファイルを展開すると、OVMF.fdファイルがあります。これを作業ディレクトリ$HOME/gnu-efiにコピーしておきます。

ディスクイメージファイルの作成とEFIファイルのコピー

Making a disk imageを参考にして、QEMUで使用するディスクイメージを作成し、これまでビルドしたEFIアプリケーション(*.efiファイル)をそこへコピーします。この作業の大半を自動化するMakefileがあるので、コピーして少々修正することで下記のようなMakefileを作成します。ファイル名はMake_Disk_Image.mkとし、作業ディレクトリ$HOME/gnu-efiに作成しておきます。

FAT_SECTORS:=65536
DISK_SECTORS:=69632
DISK_START:=2048
DISK_END:=67584
 
TEMP_IMG := temp.img
PARTED := sudo parted 
PARTED_PARAMS := -s -a minimal
 
partition.img: $(FILES)
    dd if=/dev/zero of=$(TEMP_IMG) bs=512 count=$(FAT_SECTORS)
    mformat -i $(TEMP_IMG) -h 32 -t 32 -n 64 -c 1 ::
    mcopy -i $(TEMP_IMG) $(FILES) ::
    cp $(TEMP_IMG) $@
    rm $(TEMP_IMG)
 
hd.img: partition.img
    dd if=/dev/zero of=$(TEMP_IMG) bs=512 count=$(DISK_SECTORS)
    $(PARTED) $(TEMP_IMG) $(PARTED_PARAMS) mklabel gpt
    $(PARTED) $(TEMP_IMG) $(PARTED_PARAMS) mkpart EFI FAT16 $(DISK_START)s $(DISK_END)s
    $(PARTED) $(TEMP_IMG) $(PARTED_PARAMS) toggle 1 boot
    dd if=partition.img of=$(TEMP_IMG) bs=512 obs=512 count=$(FAT_SECTORS) seek=$(DISK_START) conv=notrunc
    cp $(TEMP_IMG) $@
    rm $(TEMP_IMG)

makeターゲット

  • hd.img: ディスクイメージファイル。ゲストからディスクデバイスとして扱われる。ブータブルパーティションを1つだけ含む。下記パーティションイメージを埋め込む。
  • partition.img: FATパーティションイメージファイル。これに対してmcopyで使用するファイルをコピー。

make実行

コピーしたいEFIアプリケーションファイルを忘れずにFILES変数に指定し、makeを実行します:

$ cd $HOME/gnu-efi
$ make -f Make_Disk_Image.mk hd.img FILES=gnu-efi/gnu-efi-3.0/apps/*.efi

ディスクイメージの内容確認

losetupコマンドでディスクイメージをloopデバイスに関連付けます。 作業が終わったらlosetup -dで関連付けを解除します。

$ sudo losetup -o`expr 2048\*512` /dev/loop0 hd.img
# ubuntuだと自動でマウントされるので
# EFIファイルが格納されていることを確認する。
$ sudo losetup -d /dev/loop0

QEMU起動

ここまでで準備が整ったのでQEMUを起動します:

$ cd $HOME/gnu-efi
$ qemu-system-x86_64 -bios OVMF.fd -m 512 -cpu kvm32 -vga cirrus -monitor stdio -hdb hd.img

下のような画面が表示されます:

f:id:shinaisan:20140523071740p:plain

入力fs0:を与え、lsコマンドを実行すると、先ほどコピーしたEFIファイルが確認できます。

f:id:shinaisan:20140523071732p:plain

EFIアプリケーションの実行はEFIファイル名を入力するだけで可能です。(例: t.efi, t7.efi, printenv.efi)

f:id:shinaisan:20140523071737p:plain

これでQEMUによるEFIアプリケーションの実行環境が整いました。