OSv CLIにコマンドを追加しよう

OSv CLIの使い方 - かーねる・う゛いえむにっきで見たとおり、現状CLIのコマンドは非常に少なく、REST APIを直接呼び出して一部の操作を行わざるを得ない状態です。

そこで、コマンドを足してOSvへコントリビューションしてみるのは如何でしょうか。
例えば、dateコマンドは以下のような非常に簡単なluaスクリプトで出来ています:

local cmd = {}

cmd.desc = [[print the system date and time]]
cmd.help = [[Usage: date

Print OSv reported date.]]

cmd.main = function()
  local content, status = osv_request({"os", "date"}, "GET")
  osv_resp_assert(status, 200)
  io.write(content, '\n')
end

return cmd

osv_request()によってREST APIが呼び出され、結果がcontentに代入されてio.write()でコンソールに出力されています。
コマンドを定義するluaスクリプトはmodules/cli/commands/に コマンド名.lua という名前で配置することになっています。

まだまだapiコマンド以外からアクセス出来ないREST API上の機能がいくつもあるので、ぜひ新しいコマンドを作成してみて下さい。

OSv CLIの使い方

リモートCLIについて - かーねる・う゛いえむにっきでも紹介したCLIの使い方についてです。

リモートCLIに必要なもの

まず、Linuxホスト側でCLIをインストールする必要があります(参照)。
その上で、httpserver(REST APIサーバ)を含むOSvのイメージを実行する必要があります。

Capstan経由で配布されているディスクイメージやosv.ioで配布されているディスクイメージは全てhttpserverを含んだイメージですが、OSvをソースコードからビルドする場合はhttpserverを明示的に指定する必要があります。

つまり、

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive
make image=ruby-publify

ではなく、

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive
make image=ruby-publify,httpserver

のようにビルドする必要があります。

CLI on OSvに必要なもの

CLIとアプリケーションとで1つのコンソールを共有するので、アプリケーションと同時にCLIを実行出来るのはアプリケーションが継続的なコンソール出力を行わない・コンソール入力を要求していない場合に限ります(OSvに切り替え可能な仮想コンソールはありません)。

コンソールの使用が競合してもCLIとアプリケーションは正常に動作しますが、出力や入力が入り交じって使えないものになってしまいます。

例えばTomcatCLIを実行する場合は、

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive
make image=tomcat,cli

のようにビルドするとTomcatCLIが同時に起動されるイメージを作る事が出来ます。

Capstanで配布されているイメージとしてcloudius/osvというCLIのみを含んだものがありますが、このイメージではJavaや他の言語ランタイムを一切含んでいないため、簡単にユーザプログラムを追加・実行するのが難しくあまり役に立ちません(※ネイティブバイナリを試すことは簡単にできます)。

CLIの使い方

help

help でコマンド一覧を表示します。
help <コマンド名>でコマンド毎の使い方を表示します。

exit

OSを停止します。poweroffと同じ動作になります。

Unix風コマンド群

cat, cd, date, df, dmesg, echo, free, ls, mkdir, poweroff, reboot, rm, top, uptimeについては同名のUnixコマンド風の動作を行います(引数などがPOSIX準拠になっているわけではありません)。

api

他のコマンド群はREST APIをラップして通常のUnixコマンドなどのように振る舞うのに対し、apiコマンドはユーザが直接REST APIを操作するためのものです。
使い勝手は他のコマンド群に劣りますが、現状ではapiコマンドを経由してしかアクセス出来ない機能が多数存在するため、このコマンドを使う事がほとんど必須になります。

api でos, jvm, networkなどのAPIクラス名の一覧が表示されます。

api <クラス名> でAPIクラスが持つメソッドが表示されます。この時、HTTPメソッド種別が特に書いていない説明の行はGETメソッドで、[POST]と書かれている行はPOSTメソッドになります。

GET以外のHTTPメソッドを実行するには、api -m のように引数を渡す必要があります。

例えばos.rebootを実行する場合は以下のようになります:

# api -m POST os reboot

POSTにパラメータが必要な場合は以下のように「変数名=値」の形式で指定します。

/# api os hostname
osv.local
/# api -m POST os hostname name=osv1

/# api os hostname
osv1

POSTに必要な変数名などの細かい情報はCLIのヘルプには表示されないので、WebブラウザでRESTサーバへ接続してREST APIのドキュメントを読んで下さい。
REST APIのドキュメントは http://:8000/ へアクセスして、左側のRESTのタブを選ぶ事で読むことが出来ます。

常に最新版がアップロードされているとは限りませんが、REST APIのドキュメントはこちらにミラーされています:OSv API

Capstanの使い方

CapstanはGo言語で実装されたOSvのデプロイツールです。
各種アプリケーションを含んだディスクイメージを1コマンドでダウンロードし、ローカルのKVMVirtualBoxなどへデプロイすることが出来ます。

以下に使い方を紹介します。

Capstanの対応環境

対応OSはLinux, Mac, Windowsの3つです。
対応しているハイパーバイザIaaSはQEMU(可能ならKVM)、VirtualBoxVMwareGoogle Compute Engineです。
ハイパーバイザを指定しない場合のデフォルトは、LinuxQEMU(可能ならKVM)、Mac, WindowsVirtualBoxです。

インストール方法

Linux / Mac
curl https://raw.githubusercontent.com/cloudius-systems/capstan/master/scripts/download | bash

コマンドのヘルプ(capstan help)

helpでコマンドの一覧を、help <コマンド名>でコマンドの詳細と引数の説明を読むことが出来ます。

capstan help
capstan help run

リモートイメージの検索(capstan search)

search <アプリ名>でアプリ名を含むリモートイメージの検索を行います。
searchだけで実行すると全てのリモートイメージの一覧を表示します。

capstan search tomcat
capstan search

リモートイメージのダウンロード(capstan pull)

pull <リモートイメージ名>でイメージをダウンロードします。

capstan pull cloudius/osv-tomcat

ダウンロード済みリモートイメージの一覧(capstan images)

ダウンロード済みのイメージ一覧を表示します。
ダウンロード済みのイメージはrunコマンドにより再ダウンロードの必要無く実行出来ます。

capstan images

リモートイメージ・停止済みインスタンスの実行(capstan run)

run <リモートイメージ名>でリモートイメージをダウンロード&実行します。既にダウンロードされている場合はダウンロード済みイメージを使用します。

run <インスタンス名>で停止済みインスタンスを実行します。

引数無しのrunではカレントディレクトリのCapstanfileからbuildコマンドで生成されたインスタンスを実行します。

以下にオプション引数の説明をします。

  • -i <インスタンス名>でインスタンス名の変更を行います。デフォルト値はリモートイメージ名から生成されます。
  • -p <ハイパーバイザ名>でハイパーバイザの指定を行います。有効な値はqemu, vbox, vmw, gceのいずれかになります。Linuxのデフォルト値はqemu、それ以外はvboxです。
  • -m <メモリサイズ>でメモリサイズの変更を行います。デフォルト値は1Gです。
  • -c でCPU数の変更を行います。デフォルト値は2です。
  • -n <ネットワークタイプ>でネットワーク構成の変更を行います。有効な値はnat, bridge, tapのいずれかになります。デフォルト値はnatです。
  • -b <ブリッジ名>でブリッジ先デバイス名の変更を行います。デフォルトネットワークタイプはnatなので使用されませんが、デフォルト値はvirbr0です。
  • -f '<ローカルポート番号>:'でnat環境でのポートフォワード設定を行います。
capstan run cloudius/osv
capstan run -m 4G -c 4 -f '8000:8000' -f '8081:8081' cloudius/osv-tomcat

インスタンスの一覧(capstan instances)

instancesでインスタンスの一覧を表示します。

capstan instances

実行中インスタンスの停止(capstan stop)

stop <インスタンス名>で実行中インスタンスを停止します。

capstan stop cloudius-osv-tomcat

インスタンスの削除(capstan delete)

delete <インスタンス名>でインスタンスを削除します。

capstan delete cloudius-osv-tomcat

OSvでLuaインタープリタを動かす

OSvは、ハイパーバイザやIaaSプラットフォームへアプリケーションをデプロイすることに特化した軽量OSです(※詳しくはこちら)。

OSvでは簡易CLIを実装するためにLua言語が使用されています。
この記事では、CLI以外の任意のLuaプログラムをOSv上で実行してみます。

OSvのビルド

OSvのgitリポジトリからソースコードをcloneします。

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive

OSvに含まれるLuaのビルドスクリプトはliblua.soだけをOSvインスタンスへインストールするようになっており、luaコマンドがインストールされません。
取り敢えずluaコマンドを実行してインタラクティブLuaプログラムを実行してみたいので、以下のようなパッチを当てます。

diff --git a/modules/lua/Makefile b/modules/lua/Makefile
index 64ea417..88ae0e6 100644
--- a/modules/lua/Makefile
+++ b/modules/lua/Makefile
@@ -33,12 +33,13 @@ MAIN=src/liblua.so

 # Local executable used for LuaRocks
 LUA_BIN=$(BDIR)/lua
+LUA_BIN_SO=$(BDIR)/lua.so
 LUA_ROCKS_BIN=$(BDIR)/luarocks

 # List of Lua modules, each module has its own target
 LUA_MODULES=LuaSocket LuaJSON Lua_stdlib LuaFileSystem LuaPath LuaSec

-module: $(MAIN) $(LUA_MODULES)
+module: $(MAIN) $(LUA_MODULES) $(LUA_BIN_SO)

 $(MAIN): $(SRCS)
 	cd src && $(MAKE)
@@ -46,6 +47,10 @@ $(MAIN): $(SRCS)
 $(LUA_BIN): $(MAIN) | $(BDIR)
 	cp src/lua $(LUA_BIN)

+$(LUA_BIN_SO):
+	cd src && gcc -o lua.so lua.o liblua.a -lm -ldl -shared
+	cp src/lua.so $(LUA_BIN_SO)
+
 $(LUA_ARCHIVE): | $(DOWNLOAD)
 	cd $(DOWNLOAD) && \
 	 curl --remote-name --remote-time $(LUA_DOWNLOAD)
diff --git a/modules/lua/module.py b/modules/lua/module.py
index b15ebbc..0d3b6b0 100644
--- a/modules/lua/module.py
+++ b/modules/lua/module.py
@@ -8,3 +8,5 @@ usr_files.add('${OSV_BASE}/modules/lua/out').to('/usr') \
 	.exclude('etc/*') \
 	.exclude('lib/luarocks/**') \
 	.exclude('share/lua/*/luarocks/**')
+
+usr_files.add('${OSV_BASE}/modules/lua/out/bin/lua.so').to('/lua.so')

OSvをLua入りでビルドします。

make image=lua

OSv上でLuaを実行

scripts/run.pyでOSvインスタンスKVM上で起動するとLuaインタプリタが起動してインタラクティブLuaスクリプトが実行出来ます。

./scripts/run.py -n -e /lua.so
OSv v0.16-14-g2514672
eth0: 192.168.122.89
Lua 5.2.3  Copyright (C) 1994-2013 Lua.org, PUC-Rio
>
>
> print("Hello world")
Hello world
> print(type("Hello world"))
string
> = 1 + 2
3

リモートCLIについて

OSvには簡易シェルとしてLua CLIが用意されていて、capstan経由でLua CLIしか入ってないVMイメージが配布されていたりするのですが、実はこのCLIは全てREST API上に実装されています。

つまり、常にhttp://127.0.0.1:8000/へRESTを送ってシステムのAPIを叩いており、そのままこのCLI実装をOSvの外側に持って行けばリモートCLIとして使う事が出来ます。

そもそもそういう意図で実装されていたのですが、最近までOSvの外側で動かすためのテストと配布イメージ作成が行われていませんでした。

最近になってLinux環境にインストール可能な配布イメージを作成出来るようになったので、ビルド方法・インストール方法・使い方をご紹介します。

リモートCLIのビルド方法

ビルド済みイメージを後述のURLから配布しますので、通常この作業は必要ありません。
ディストリビューションでパッケージを作る用途などにお使い下さい。

なお、このビルド方法は今の所Fedora20でのみ動作が確認されています。

git clone https://github.com/cloudius-systems/osv.git
cd osv
git submodule update --init --recursive
sudo scripts/setup.py
cd modules/cli
make rpm

以上のコマンドでrpmbuild/RPMS/x86_64/osv-cli-1.0-1.fc20.x86_64.rpmRPM形式で、rpmbuild/SOURCES/osv-cli-1.0.tar.gzにtar.gz形式で配布イメージが出力されます。

ビルド済みイメージのダウンロード

osv-cli-1.0-1.fc20.x86_64.rpm
osv-cli-1.0.tar.gz

配布イメージのインストー

RPMの場合
yum install osv-cli-1.0-1.fc20.x86_64.rpm
tar.gzの場合
tar xpf osv-cli-1.0.tar.gz
mv osv-cli-1.0/usr/lib64/osv-cli /usr/lib64
echo "(cd /usr/lib64/osv-cli && exec ./cli \$@)" > /usr/bin/cli
chmod a+rx /usr/bin/cli

使い方

シェル1(OSvを起動)
$ capstan run -n 'bridge' cloudius/osv-tomcat
Created instance: cloudius-osv-tomcat
OSv v0.16
eth0: 192.168.122.72

IPアドレスをメモ。

シェル2(リモートCLIを起動)
$ cli -a http://192.168.122.72:8000
/# ls
dev/             etc/             init/            java/
java.so          libhttpserver.so libtools.so      libuutil.so
libzfs.so        proc/            tmp/             tools/
usr/             zfs.so           zpool.so
/# cat etc/hosts
127.0.0.1 localhost osv.local

/# api os version
v0.16
/#

次回予告

今回はRESTサーバに対するセキュリティ設定(鍵認証設定)、CLIの使い方に言及しなかったので、次回はこのあたりを解説していきます。

追伸

Luaで実装されたRESTベースのシェルってやつのソースコードが読んでみたいという方、このへんです。

mruby on EFI Shellのビルド

※この記事では後々mrbgemsを追加していく事などを意図してmrubyをビルドする事に拘っているが、単にmrubyをUEFIで試してみたいだけならば自分でビルドする必要はない。ここのHow to useからリンクされている"download page"をクリックすればダウンロードできる。

前回の記事をベースにmruby on EFI Shellのビルドを行う。

他のmruby on EFI Shellのビルド方法のメモ()は日付が2年前になっておりディストリやgccのバージョン、EDK2のコードがこのときからかなり変わってはいるが、手順に大きな変更は見られなかった。

まず、Conf/tools_def.txtのGCC44_ALL_CC_FLAGSから-Werrorを取り除く。

DEFINE GCC44_ALL_CC_FLAGS            = -g -fshort-wchar -fno-strict-aliasing -Wall -Wno-array-bounds -ffunction-sections 
-fdata-sections -c -include AutoGen.h -DSTRING_ARRAY_NAME=$(BASE_NAME)Strings

AppPkgにmrubyを追加する。

cd AppPkg/Applications
git clone https://github.com/masamitsu-murase/mruby_on_efi_shell.git mruby
cd mruby
git submodule update --init --recursive
cd mruby
rake
cd ..
ruby override/override.rb

AppPkg/AppPkg.dscを開いて[Components]にAppPkg/Applications/mruby/mruby.infを足す。

diff --git a/AppPkg/AppPkg.dsc b/AppPkg/AppPkg.dsc
index 424434c..e34ceb6 100644
--- a/AppPkg/AppPkg.dsc
+++ b/AppPkg/AppPkg.dsc
@@ -112,6 +112,7 @@
   AppPkg/Applications/Main/Main.inf          # Simple invocation. No other LibC
   AppPkg/Applications/Enquire/Enquire.inf    #
   AppPkg/Applications/ArithChk/ArithChk.inf  #
+  AppPkg/Applications/mruby/mruby.inf

 #### A simple fuzzer for OrderedCollectionLib, in particular for
 #### BaseOrderedCollectionRedBlackTreeLib.

LibCにパッチを当てる。

diff --git a/StdLib/LibC/Main/Main.c b/StdLib/LibC/Main/Main.c
index b203d15..f22edcf 100644
--- a/StdLib/LibC/Main/Main.c
+++ b/StdLib/LibC/Main/Main.c
@@ -31,7 +31,7 @@
 #include  <MainData.h>
 #include  <unistd.h>

-extern int main( int, char**);
+extern int EFIAPI main( int, char**);^M
 extern int __sse2_available;

mrubyをビルド。

build

前回と同様の手順でovmfディレクトリにmruby.efiをコピーしてくる。

cd ~/ovmf
cp ~/edk2/Build/AppPkg/DEBUG_GCC49/X64/mruby.efi .

それっぽいサンプルコードを用意する。

sort.rb
[10,2,4,1].sort.each{|i| p i}
fib.rb
def fib(n)
    return 0 if n == 0
    return 1 if n == 1
    fib(n-2) + fib(n-1)
end

p fib(10)

QEMUを起動、UEFIから実行してみる。

qemu-system-x86_64 -nographic -bios OVMF.fd -hda fat:.

UEFI Interactive Shell v2.0
EDK II
UEFI v2.40 (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 1 seconds to skip startup.nsh or any other key to continue.
Shell> fs0:
FS0:\> dir
Directory of: FS0:\
02/10/2014  12:42               4,210  LICENSE
12/03/2014  06:27                  96  fib.rb
02/10/2014  12:30              10,573  README
02/10/2014  12:42           1,048,576  OVMF.fd
12/03/2014  06:28                  30  sort.rb
02/10/2014  12:42                 130  BUILD_INFO
12/03/2014  06:47             525,440  mruby.efi
12/02/2014  22:06               1,540  NvVars
          8 File(s)   1,590,595 bytes
          0 Dir(s)
FS0:\> mruby fib.rb
55
FS0:\> mruby sort.rb
1
2
4
10

EDK2でUEFI Application開発環境整備とQEMU Virtual FAT

もうちょっと応用的な内容にトライしようと思ったら久々の開発環境整備で手間取ったので、まずどうやって試すのか、について書いてお茶を濁す。

開発環境はFedora 20 + gcc-4.9 from rawhideという変則な環境だが、新しめのパッケージが使われているディストリならこれと状況は大きく変わらない。
なおアーキテクチャはX64とする。32bit環境については取り扱わない。

まずはEDK2のお決まりのビルド作業について。

git clone https://github.com/tianocore/edk2.git
cd edk2
make -C edk2/BaseTools
. edksetup.sh

Conf/target.txtを開いて以下のように変更する。

ACTIVE_PLATFORM       = AppPkg/AppPkg.dsc
TOOL_CHAIN_TAG        = GCC49
TARGET_ARCH              = X64

edksetup.shが用意したbuildコマンドにより、AppPkg.dscで指定された*.infで列挙されているプログラムがビルドされる。
この独特のビルドシステムはLinux標準のやり方と全く違うので覚える必要がある。

build

ビルド終了するとBuildディレクトリにバイナリが出力される。

$ ls Build/AppPkg/DEBUG_GCC49/X64/
AppPkg               GetNetByAddr.debug           OrderedCollectionTest.efi
ArithChk.debug       GetNetByAddr.efi             RawIp4Rx.debug
ArithChk.efi         GetNetByName.debug           RawIp4Rx.efi
DataSink.debug       GetNetByName.efi             RawIp4Tx.debug
DataSink.efi         GetServByName.debug          RawIp4Tx.efi
DataSource.debug     GetServByName.efi            RecvDgram.debug
DataSource.efi       GetServByPort.debug          RecvDgram.efi
Enquire.debug        GetServByPort.efi            SetHostName.debug
Enquire.efi          Hello.debug                  SetHostName.efi
GetAddrInfo.debug    Hello.efi                    SetSockOpt.debug
GetAddrInfo.efi      Main.debug                   SetSockOpt.efi
GetHostByAddr.debug  Main.efi                     ShellPkg
GetHostByAddr.efi    MdeModulePkg                 SocketDxe.debug
GetHostByDns.debug   MdePkg                       SocketDxe.efi
GetHostByDns.efi     OobRx.debug                  StdLib
GetHostByName.debug  OobRx.efi                    TOOLS_DEF.X64
GetHostByName.efi    OobTx.debug                  UefiCpuPkg
GetNameInfo.debug    OobTx.efi                    WebServer.debug
GetNameInfo.efi      OrderedCollectionTest.debug  WebServer.efi

次に、UEFI Applicationを実行するためQEMUUEFIを実行出来るようにする。
OVMFのページから「X64 (zip)」を選択してダウンロードする。

unzipコマンドで解凍してOVMF.fdを取り出す。

mkdir ovmf
cd ovmf
unzip -x ~/OVMF-X64-*.zip
$ ls
BUILD_INFO  LICENSE  OVMF.fd  README

ここに、先ほどビルドしたUEFI Applicationをコピーしてくる。

cp ~/edk2/Build/AppPkg/DEBUG_GCC49/X64/*.efi .

これで全ての準備が整ったのでQEMUを実行する。
このとき、VGAは無効にしてシリアルコンソールで立ち上げ、ファームウェアにはOVMF(UEFIファームウェア)を指定、ハードディスクイメージにVirtual FATを指定する。Virtual FATのパスには.(カレントディレクトリ)を指定。

qemu-system-x86_64 -nographic -bios OVMF.fd -hda fat:.

ネットワークブートがタイムアウトするのに暫く時間がかかるがじっと待つ。

UEFI Shellが起動したらFS0:を参照するとカレントディレクトリがみえるはず。

UEFI Interactive Shell v2.0
EDK II
UEFI v2.40 (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 1 seconds to skip startup.nsh or any other key to continue.
Shell> fs0:
FS0:\> dir
Directory of: FS0:\
12/03/2014  04:21             186,144  GetServByName.efi
12/03/2014  04:21             311,392  OobTx.efi
12/03/2014  04:21             301,952  RawIp4Tx.efi
12/03/2014  04:21             180,832  OrderedCollectionTest.efi
02/10/2014  12:42               4,210  LICENSE
12/03/2014  04:21             293,792  GetAddrInfo.efi
12/03/2014  04:21             243,968  SetHostName.efi
12/03/2014  04:21             327,648  OobRx.efi
12/03/2014  04:21             226,016  GetServByPort.efi
02/10/2014  12:30              10,573  README
12/03/2014  04:21             144,192  ArithChk.efi
12/03/2014  04:21             450,720  GetNameInfo.efi
12/03/2014  04:21             384,320  DataSource.efi
12/03/2014  04:21             368,128  GetHostByDns.efi
12/03/2014  04:21             345,888  DataSink.efi
02/10/2014  12:42           1,048,576  OVMF.fd
12/03/2014  04:21             399,008  GetNetByAddr.efi
12/03/2014  04:21             270,208  Enquire.efi
12/03/2014  04:21             326,848  RawIp4Rx.efi
12/03/2014  04:21             127,232  SocketDxe.efi
12/03/2014  04:21             362,432  GetNetByName.efi
12/03/2014  04:21             411,552  GetHostByAddr.efi
02/10/2014  12:42                 130  BUILD_INFO
12/03/2014  04:21             375,456  GetHostByName.efi
12/03/2014  04:21             450,720  SetSockOpt.efi
12/03/2014  04:21             341,056  RecvDgram.efi
12/03/2014  04:21              18,496  Hello.efi
12/03/2014  04:21             341,952  WebServer.efi
12/03/2014  04:21             142,880  Main.efi
12/02/2014  19:33               1,540  NvVars
         30 File(s)   8,397,861 bytes
          0 Dir(s)

ビルドしてきたサンプルプログラムを実行してみる。

FS0:\> Hello
Hello there fellow Programmer.
Welcome to the world of EDK II.

なお、このVirtual FATはQEMU起動時の時点で構築されるので、ビルドしては新しいプログラムを試すという使い方をすべきではない。毎回QEMUを再起動すべきである。