UEFI(Unified Extensible Firmware Interface)はOSとファームウェアの間の橋渡しを行うインターフェースの仕様です。今回はQEMU/KVMで、このUEFIをサポートしたファームウェアを使用する方法を紹介します。
UEFIとQEMU/KVMとOVMF
いわゆる「PC/AT互換機」とも呼ばれるIntel系CPUを搭載したPCでは、電源投入後からハードウェアの初期化、ブートローダーの起動などを「BIOS」と呼ばれるファームウェアが担っていました[1]。UEFIはこのBIOSを置き換えるための仕様で、機能の強化や起動の高速化、業界団体による標準化などが行われています。ここ数年のx86マシンであればほぼすべてUEFIファームウェアに置き換わっていますし、ARMプラットフォームでもよく使われるU-Bootが最近UEFIをサポートするようになりました。本連載の読者の皆様も、一度ぐらいはUEFIファームウェアに触れていることでしょう。
Ubuntuも11.10ぐらいからUEFIによる起動をサポートするようになりました。しかしながら初期の頃は、インストーラーがUEFIシステムパーティションを壊してしまうなど、いろいろと問題もあったのです。現在サポートしているリリースであれば、基本的には問題なくUEFIシステム上にUbuntuをインストールできるようになっています。ただし、UEFIファームウェアの実装の違いやデバイス・他のOSとの相性の違いによって、Ubuntuをうまくインストールできない、インストールできたとしてもいくつかのデバイスが動かない、といった場合があることも事実です[2]。UEFIに限ったことではありませんが、Ubuntuをインストールする場合はあらかじめシステム上のデータのバックアップをとり、何かあったら復元できるようにしておきましょう。
さて、普段は他のOSで生活し、Ubuntuは仮想マシン上でのみ使用している方もいることでしょう。その場合はおそらくUEFIではなくBIOSを用いたインストールになるはずです。仮想マシン上でもUEFIの恩恵を受けたい場合は、仮想マシンの設定を明示的に変更する必要があります。たとえばVirtualBoxであれば、2009年にリリースされた3.1の頃からEFIファームウェアを使った起動ができるようです[3]。QEMU/KVMの場合は、その本体にはUEFIファームウェアは含まれていません。その代わり「-bios
」オプションで使用するファームウェアを指定できます。この「-bios
」オプションにFLOSSなUEFIファームウェアの実装であるOVMF(Open Virtual Machine Firmware)を指定すれば、UEFIブートできるというわけです。
そこで今回は、ホストOSをUbuntu 16.04 LTSとした上でQEMU/KVMを用いる時に、ゲストOSをUEFIファームウェアを用いて起動する方法を紹介します。
OVMFのインストールと基本的な使い方
QEMU/KVM標準のBIOSファームウェアは「SeaBIOS」です。seabiosパッケージから提供されていますし、QEMUをインストールすれば自動的にこのパッケージがインストールされます。
普通にQEMU/KVMから仮想マシンを起動しただけだとSeaBIOSが非対話モードで起動しますが、「-boot menu=on
」オプションを付けて起動すると、F12キーでBISOメニュー画面が表示されます[4]。
メニューはシンプルですがSeaBIOSそのものの設定項目は多彩で、ブートスプラッシュ画像などはQEMUの「-boot
」オプション経由でも指定できます。
従来のBIOSの話はこれくらいにしておいて、UEFIファームウェアをインストールしましょう。Ubuntuだとovmfパッケージをインストールするだけです。
OVMFはUEFIファームウェアのFLOSSな実装です。UEFIの前身であるEFIを開発していたIntelが、EFI仕様の「好ましい実装」としてTianoCoreを開発していました。EFIがUEFIになるにあたってEFIアプリケーションの開発環境を整理したものが現在のEDK IIであり、EDK IIによるサンプルアプリケーションのひとつがOVMFです。どちらもBSDライセンスで提供されています[5]。よってEDK IIの最新のソースコードをダウンロードしてきて、自前のOVMFファイルを作成することも可能です。
QEMU/KVMでOVMFを指定する方法はいくつか存在します。一番手っ取り早いのは「-bios
」オプションにファームウェアのファイル名を渡す方法です。
ブート順やブートデバイスの指定だけでなく、言語の変更やネットワーク設定、セキュアブートの設定など、UEFIらしくさまざまな機能に対応しています。ただし「-bios
」オプションでファームウェアを指定するだけだと、設定は保存されません。一度QEMUプロセスを終了すると、リセットされてしまいます。もしUEFIファームウェアの設定画面の内容を保存したかったら、「-pflash
」オプションでファームウェアを指定する必要があります。この場合、設定内容は指定したファイルの変数領域(0x00000
から0x20000
まで)に書き込まれることになります。よって、あらかじめファイルをコピーしておきましょう。
この状態でUEFIの設定インターフェースを開き、何がしかの設定を行った上で保存してQEMUを終了してください。OVMF.fd
のバイナリダンプを確認するとオリジナルのそれと変わっていることがわかるかと思います。
OVMF.fd
は前半に変数データを保存するNVRAM領域を、後半にファームウェアのコード本体を記録しています。よってOVMF.fd
全体をコピーするのではなく、不変なコード領域は共有し、NVRAM領域だけインスタンスごとに保存したいところです。実はovmfパッケージは、コード領域とNVRAM領域がそれぞれ別になったファイルも用意されています。それを使えば、サイズの小さいNVRAM領域だけ流用することも可能です。
後述するvirt-managerなどは、このようなオプションを自動的に付加する作りになっています。
ちなみに、UEFIには「UEFI Shell」と呼ばれるシェル環境が用意されています。設定メニューの「Boot Manager」から「EFI Internal Shell」を選択すれば、EFI Shellが立ち上がるはずです。このシェルは、おもにUEFIアプリケーションのデバッグに使われるものですが、ブートローダーが起動する前のハードウェアの状態や設定の確認、スクリプトの実行なども行えます。「help -b
」で使用できるコマンド一覧が表示されますので、一度遊んでみるといいでしょう。なお「-b
」オプションは、実行結果が画面上を流れすぎてしまうことを抑止するオプションです。UEFI Shell上のたいていのコマンドで使用できます。
UEFIシステムにUbuntuをインストールする
実際にUEFIファームウェアを有効にした仮想マシンにUbuntu 16.04 LTSをインストールしてみましょう。
ひとつ注意点として、一度BIOSモードで起動した上でインストールした仮想マシンイメージは、BIOSのファームウェアをOVMFに変更するだけでは起動できません。Ubuntuのインストーラーが、BIOSモードの時はUEFIシステムパーティションを作らず、NVRAM領域の変更も行わないためです。もしBIOSモードで作成済みの仮想マシンイメージをUEFIに移行したいのであれば、何手間かの作業が必要になりますが、今回の記事では扱いません。
QEMUコマンドを使ってインストールする
もっともプリミティブなやり方として、QEMUコマンドを直に叩いてUbuntuをインストールしてみましょう。
これだけです。あとはインストーラーから、普通の手順でインストールを行い、インストールが完了したらシャットダウンしてください。次回以降は、作成したイメージを指定して起動します。
実際に起動したイメージでdmesg
コマンドを実行すれば、UEFIファームウェアを使っていることがわかります[6]。
BIOSで起動した時は上記EFI関連のメッセージは表示されません。BIOS関連のメッセージとして、次の内容が表示されます。ちなみにUEFIファームウェアを使った場合も、下記のメッセージは表示されます。
なおGRUBの最初の画面がどうなっているかでも、UEFIかどうかを判断できます。
virt-managerを使ってインストールする
Ubuntu 16.04 LTSにインストールされるvirt-managerであれば、GUIからUEFIの使用を設定可能です。
あとは画面の指示に従って起動&インストールしてください。OVMFのNVRAM領域は、/var/lib/libvirt/qemu/nvram/
以下に「ドメイン名_VARS.fd
」という名前で保存されます。
GNOME Boxesを使ってインストールする
GNOME BoxesはGUIからお手軽に仮想マシンを作成・管理するGTK+3製のツールです。バックエンドにQEMU/libvirtを使っているため、一般ユーザー向けのvirt-managerだと思えばよいでしょう[7]。GNOME Boxes自体は、UEFIファームウェアへの変更に対応していません。ただしバックエンドがvirt-managerと同じQEMU/libvirtであることからもわかるように、単に設定UIがないだけで、UEFIファームウェアを使った起動ができないわけではありません。そこで、最後の例としてGNOME BoxesでUEFI対応仮想マシンを作る例を紹介します。
次のような手順でGNOME Boxesにおける、UEFIファームウェア対応のイメージを作成できます[8]。
ここまでで仮想マシンの設定ファイルやイメージの雛形ができあがった状態になります。以降はホストの端末から、virsh
コマンドを使って操作します。まずは作成済みの仮想マシンのリストを表示しましょう。GNOME Boxesはセッションハイパーバイザーに仮想マシンを作成するので、virsh
コマンドには常に-c qemu:///session
を渡します。
仮想マシン名(ドメイン名)が「boxes-unknown
」であることがわかります。そこで、このドメイン用のNVRAMファイルを用意した上で、設定XMLファイルを編集しましょう。
ファイルのコピー先は各自の環境にあわせてください。ディレクトリがなかったら作成します。またファイル名はドメイン名にあわせておくと良いでしょう。ファイルの先頭のほうにあるos
タグの中身を次のように変更します。
追加したのはloader
タグとnvram
タグの二つです。まずコード領域をloader
タグで指定します。こちらは読み込み専用なので、readonly
フラグもつけています。NVRAM領域のファイルを指定するのがnvram
タグです。属性については/usr/share/libvirt/schemas/domaincommon.rng
にも記載があります。
あとはこのファイルを保存した上で起動すれば、UEFIモードで起動するはずです。ちなみにこれらのXMLファイルは~/.config/libvirt/qemu/
以下に保存されています。