Ubuntu Weekly Recipe

第441回QEMU/KVMでUEFIファームウェアを使う

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をインストールすれば自動的にこのパッケージがインストールされます。

$ sudo apt install qemu-kvm
$ apt policy seabios
seabios:
  インストールされているバージョン: 1.8.2-1ubuntu1
  候補:               1.8.2-1ubuntu1
  バージョンテーブル:
 *** 1.8.2-1ubuntu1 500
        500 http://jp.archive.ubuntu.com/ubuntu xenial/main amd64 Packages
        500 http://jp.archive.ubuntu.com/ubuntu xenial/main i386 Packages
        100 /var/lib/dpkg/status

普通にQEMU/KVMから仮想マシンを起動しただけだとSeaBIOSが非対話モードで起動しますが、-boot menu=onオプションを付けて起動すると、F12キーでBISOメニュー画面が表示されます[4]⁠。

$ qemu-system-x86_64 -boot menu=on
図1 メニューと言ってもブートデバイスの選択のみ
画像

メニューはシンプルですがSeaBIOSそのものの設定項目は多彩で、ブートスプラッシュ画像などはQEMUの-bootオプション経由でも指定できます

従来のBIOSの話はこれくらいにしておいて、UEFIファームウェアをインストールしましょう。Ubuntuだとovmfパッケージをインストールするだけです。

$ sudo apt install 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オプションにファームウェアのファイル名を渡す方法です。

$ qemu-system-x86_64 -bios OVMF.fd
図2 起動時にロゴが表示される
画像
図3 ESCキーでUEFIメニュー
画像

ブート順やブートデバイスの指定だけでなく、言語の変更やネットワーク設定、セキュアブートの設定など、UEFIらしくさまざまな機能に対応しています。ただし-biosオプションでファームウェアを指定するだけだと、設定は保存されません。一度QEMUプロセスを終了すると、リセットされてしまいます。もしUEFIファームウェアの設定画面の内容を保存したかったら、-pflashオプションでファームウェアを指定する必要があります。この場合、設定内容は指定したファイルの変数領域0x00000から0x20000まで)に書き込まれることになります。よって、あらかじめファイルをコピーしておきましょう。

$ cp /usr/share/ovmf/OVMF.fd .
$ qemu-system-x86_64 -pflash ./OVMF.fd

この状態でUEFIの設定インターフェースを開き、何がしかの設定を行った上で保存してQEMUを終了してください。OVMF.fdのバイナリダンプを確認するとオリジナルのそれと変わっていることがわかるかと思います。

OVMF.fdは前半に変数データを保存するNVRAM領域を、後半にファームウェアのコード本体を記録しています。よってOVMF.fd全体をコピーするのではなく、不変なコード領域は共有し、NVRAM領域だけインスタンスごとに保存したいところです。実はovmfパッケージは、コード領域とNVRAM領域がそれぞれ別になったファイルも用意されています。それを使えば、サイズの小さいNVRAM領域だけ流用することも可能です。

$ cp /usr/share/OVMF/OVMF_VARS.fd sample.fd
$ qemu-system-x86_64 \
    -drive if=pflash,format=raw,readonly,file=/usr/share/OVMF/OVMF_CODE.fd \
    -drive if=pflash,format=raw,file=sample.fd

後述するvirt-managerなどは、このようなオプションを自動的に付加する作りになっています。

ちなみに、UEFIには「UEFI Shell」と呼ばれるシェル環境が用意されています。設定メニューの「Boot Manager」から「EFI Internal Shell」を選択すれば、EFI Shellが立ち上がるはずです。このシェルは、おもにUEFIアプリケーションのデバッグに使われるものですが、ブートローダーが起動する前のハードウェアの状態や設定の確認、スクリプトの実行なども行えます。help -bで使用できるコマンド一覧が表示されますので、一度遊んでみるといいでしょう。なお-bオプションは、実行結果が画面上を流れすぎてしまうことを抑止するオプションです。UEFI Shell上のたいていのコマンドで使用できます。

図4 OVMFのEFI Shell環境
画像

UEFIシステムにUbuntuをインストールする

実際にUEFIファームウェアを有効にした仮想マシンにUbuntu 16.04 LTSをインストールしてみましょう。

ひとつ注意点として、一度BIOSモードで起動した上でインストールした仮想マシンイメージは、BIOSのファームウェアをOVMFに変更するだけでは起動できません。Ubuntuのインストーラーが、BIOSモードの時はUEFIシステムパーティションを作らず、NVRAM領域の変更も行わないためです。もしBIOSモードで作成済みの仮想マシンイメージをUEFIに移行したいのであれば、何手間かの作業が必要になりますが、今回の記事では扱いません。

QEMUコマンドを使ってインストールする

もっともプリミティブなやり方として、QEMUコマンドを直に叩いてUbuntuをインストールしてみましょう。

$ cp /usr/share/OVMF/OVMF_VARS.fd ubuntu1_OVMF_VARS.fd
$ qemu-img create -f qcow2 ubuntu1.qcow2 32G
$ qemu-system-x86_64 -m 2G -enable-kvm \
    -drive if=pflash,format=raw,readonly,file=/usr/share/OVMF/OVMF_CODE.fd \
    -drive if=pflash,format=raw,file=ubuntu1_OVMF_VARS.fd \
    -drive if=virtio,file=ubuntu1.qcow2 \
    -cdrom ~/ダウンロード/ubuntu-16.04.1-desktop-amd64.iso

これだけです。あとはインストーラーから、普通の手順でインストールを行い、インストールが完了したらシャットダウンしてください。次回以降は、作成したイメージを指定して起動します。

$ qemu-system-x86_64 -m 2G -enable-kvm \
    -drive if=pflash,format=raw,readonly,file=/usr/share/OVMF/OVMF_CODE.fd \
    -drive if=pflash,format=raw,file=ubuntu1_OVMF_VARS.fd \
    -drive if=virtio,file=ubuntu1.qcow2

実際に起動したイメージでdmesgコマンドを実行すれば、UEFIファームウェアを使っていることがわかります[6]⁠。

$ dmesg | grep -i efi
[    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-4.4.0-38-generic.efi.signed root=...
[    0.000000] efi: EFI v2.60 by EDK II

BIOSで起動した時は上記EFI関連のメッセージは表示されません。BIOS関連のメッセージとして、次の内容が表示されます。ちなみにUEFIファームウェアを使った場合も、下記のメッセージは表示されます。

$ dmesg | grep -i bios
[    0.000000] e820: BIOS-provided physical RAM map:
...
[    0.000000] DMI: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
...

なおGRUBの最初の画面がどうなっているかでも、UEFIかどうかを判断できます。

図5 UEFIの場合はGRUBメニュー画面になる
画像
図6 BIOSの場合はUbuntuロゴが表示される
画像

virt-managerを使ってインストールする

Ubuntu 16.04 LTSにインストールされるvirt-managerであれば、GUIからUEFIの使用を設定可能です。

$ sudo apt install virt-manager
(libvirtdグループへの参加を反映するために、一度ログインしなおす)
$ virt-manager
図7 仮想マシンの最後のステップで「インストールの前に設定をカスタマイズする」をチェック
画像
図8 概要のファームウェアでOVMFファイルを指定
画像

あとは画面の指示に従って起動&インストールしてください。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]⁠。

図9 新規ボタンでイメージを作成
画像
図10 起動画面になったら一度「強制終了」
画像

ここまでで仮想マシンの設定ファイルやイメージの雛形ができあがった状態になります。以降はホストの端末から、virshコマンドを使って操作します。まずは作成済みの仮想マシンのリストを表示しましょう。GNOME Boxesはセッションハイパーバイザーに仮想マシンを作成するので、virshコマンドには常に-c qemu:///sessionを渡します。

$ virsh -c qemu:///session list --all
 Id    名前                         状態
----------------------------------------------------
 -     boxes-unknown                  シャットオフ

仮想マシン名(ドメイン名)boxes-unknownであることがわかります。そこで、このドメイン用のNVRAMファイルを用意した上で、設定XMLファイルを編集しましょう。

$ cp /usr/share/OVMF/OVMF_VARS.fd /home/shibata/.config/libvirt/qemu/nvram/boxes-uknown_VARS.fd
$ virsh -c qemu:///session edit boxes-unknown

ファイルのコピー先は各自の環境にあわせてください。ディレクトリがなかったら作成します。またファイル名はドメイン名にあわせておくと良いでしょう。ファイルの先頭のほうにあるosタグの中身を次のように変更します。

  <os>
    <type arch="x86_64" machine="pc-i440fx-wily">hvm</type>
    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
    <nvram>/home/shibata/.config/libvirt/qemu/nvram/boxes-uknown_VARS.fd</nvram>
    <boot dev="hd"/>
  </os>

追加したのはloaderタグとnvramタグの二つです。まずコード領域をloaderタグで指定します。こちらは読み込み専用なので、readonlyフラグもつけています。NVRAM領域のファイルを指定するのがnvramタグです。属性については/usr/share/libvirt/schemas/domaincommon.rngにも記載があります。

あとはこのファイルを保存した上で起動すれば、UEFIモードで起動するはずです。ちなみにこれらのXMLファイルは~/.config/libvirt/qemu/以下に保存されています。

おすすめ記事

記事・ニュース一覧