OVMF による PCI パススルー
こちらは QEMU で PCI VGA パススルーを行う方法のガイドです。カーネル 3.9 から QEMU に変更があり、グラフィックカードをパススルーすることが可能になりました。ゲームなどのグラフィック負担が重い処理を実行するときに、VM からネイティブのグラフィック性能を発揮できます。グラフィックカードをパススルーするには、2つのグラフィックカードが必要になります。片方はホストに、もう片方は VM に割り当てられます。グラフィックカードといっても、ホスト側では内蔵のグラフィックを使うことができます。また、プロセッサとマザーボードが AMD-VI/VT-D に対応していなければなりません。
目次
インストール
qemu と rpmextract をインストールしてください。カーネルにパッチを適用する必要がある場合は linux-vfioAUR もインストールします。
Gerd Hoffman のリポジトリ から edk2.git-ovmf-x64 をダウンロードしてください。
ダウンロードした圧縮ファイルを /usr
に展開:
# rpmextract.sh edk2.git-ovmf-x64-0-20150223.b877.ga8577b3.noarch.rpm # cp -R ./usr/share/* /usr/share
/usr/share/edk2.git/ovmf-x64
に以下のファイルがあることを確認してください:
$ ls /usr/share/edk2.git/ovmf-x64/*pure*.fd
/usr/share/edk2.git/ovmf-x64/OVMF-pure-efi.fd /usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd /usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd
上記は VM が使用する BIOS になります。UEFI を使っていない場合は i440fx を使う必要があったり、Intel の GPU をホストで使うときは i915 vga arbiter パッチが必要になったりします。詳しくは フォーラムスレッド を参照してください。また、マザーボードは UEFI に対応していてもグラフィックカードが UEFI に対応していない場合、こちら を見てください。
libvirt の設定
Polkit#パスワードプロンプトの迂回を参照してパスワードプロンプトを迂回するようにして、libvirt をインストールしてから libvirtd.service
を起動・有効化してください。
virt-manager を起動してハイパーバイザーを設定してください。Virtmanager で qemu のセッションに接続します。
IOMMU の有効化
BIOS の設定で AMD-VI/VT-D が有効になっていることを確認してください。
使用しているプロセッサが Intel 製の場合、ブートローダーのカーネルオプションに intel_iommu=on
を追加してください。同様に、AMD 製の場合、amd_iommu=on
を追加してください。
再起動後、IOMMU が有効になっていることを dmesg で確認します:
dmesg|grep -e DMAR -e IOMMU
[ 0.000000] ACPI: DMAR 0x00000000BDCB1CB0 0000B8 (v01 INTEL BDW 00000001 INTL 00000001) [ 0.000000] Intel-IOMMU: enabled [ 0.028879] dmar: IOMMU 0: reg_base_addr fed90000 ver 1:0 cap c0000020660462 ecap f0101a [ 0.028883] dmar: IOMMU 1: reg_base_addr fed91000 ver 1:0 cap d2008c20660462 ecap f010da [ 0.028950] IOAPIC id 8 under DRHD base 0xfed91000 IOMMU 1 [ 0.536212] DMAR: No ATSR found [ 0.536229] IOMMU 0 0xfed90000: using Queued invalidation [ 0.536230] IOMMU 1 0xfed91000: using Queued invalidation [ 0.536231] IOMMU: Setting RMRR: [ 0.536241] IOMMU: Setting identity map for device 0000:00:02.0 [0xbf000000 - 0xcf1fffff] [ 0.537490] IOMMU: Setting identity map for device 0000:00:14.0 [0xbdea8000 - 0xbdeb6fff] [ 0.537512] IOMMU: Setting identity map for device 0000:00:1a.0 [0xbdea8000 - 0xbdeb6fff] [ 0.537530] IOMMU: Setting identity map for device 0000:00:1d.0 [0xbdea8000 - 0xbdeb6fff] [ 0.537543] IOMMU: Prepare 0-16MiB unity mapping for LPC [ 0.537549] IOMMU: Setting identity map for device 0000:00:1f.0 [0x0 - 0xffffff] [ 2.182790] [drm] DMAR active, disabling use of stolen memory
デバイスへのアクセス権限をユーザーに付与
udev ルールを作成してデバイス (hdd や gpu) にアクセスする権限をユーザーに与えてください:
/etc/udev/rules.d/10-qemu-hw-users.rules
KERNEL=="sda[3-6]", OWNER="YOUR_USER", GROUP="YOUR_GROUP" KERNEL=="YOUR_VFIO_GROUPS", SUBSYSTEM=="vfio", OWNER="YOUR_USER", GROUP="YOUR_USER"
このようにすることで /etc/libvirtd/qemu.conf
で qemu のユーザーやグループを root:root よりも安全に設定できます。
GPU の分離
パススルーするカードの PCI アドレスとデバイス ID を確認してください:
lspci -nn|grep -iP "NVIDIA|Radeon"
01:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Cayman PRO [Radeon HD 6950] [1002:6719] 01:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Cayman/Antilles HDMI Audio [Radeon HD 6900 Series] [1002:aa80] 04:00.0 VGA compatible controller [0300]: NVIDIA Corporation G73 [GeForce 7600 GS] [10de:0392] (rev a1)
上記の場合、3つの PCI デバイス ID はそれぞれ 1002:6719 1002:aa80 10de:0392
となり、アドレスは 01:00.0 01:00.1 04:00.0
です。VM から直接扱わせたいデバイスの ID とアドレスをメモしてください。
VM からパススルーしたデバイスにアクセスできるようにするには、ホストのドライバーよりも前にデバイスを使えるようにしておく必要があります。vfio-pci
あるいは pci-stub
のどちらか片方のカーネルモジュールを使ってください。
vfio-pci はカーネルのバージョン 4.1 以上で利用可能で、カーネルが vfio-pci に対応しているのであれば、vfio-pci を使用することを推奨します。モジュールが利用できるかどうかは次のコマンドで確認できます:
$ modprobe vfio-pci
何も出力がされなければ、問題ありません。modprobe: FATAL: Module vfio-pci not found
というメッセージが表示される場合、下にある pci-stub
を使う方法を見てください。
vfio-pci
modprobe.d
から起動時に vfio-pci ドライバーがロードされるようにします。vfio-pci ドライバーには接続する PCI デバイスを指定する必要があります。上記の例にある3つの PCI デバイス全てを追加する場合、modprobe.d
には以下のように設定ファイルを作成します:
/etc/modprobe.d/vfio.conf
options vfio-pci ids=1002:6719,1002:aa80,10de:0392
次に、起動時に vfio-pci を使うのに必要なモジュールをカーネルからロードするようにしてください:
/etc/mkinitcpio.conf
... MODULES="vfio vfio_iommu_type1 vfio_pci vfio_virqfd" ...
初期 RAM ディスク環境に変更を保存:
# mkinitcpio -p linux
initramfs の 'base' フックを使用しているとき、MODULES 配列に指定されたモジュールはブートプロセスの初期段階でロードされます。同じことは 'systemd' フックでも 'rd.modules-load' カーネルパラメータを使ってロードするモジュールを指定することで可能です。
rd.modules-load=vfio-pci,...
設定したら再起動して、デバイスが vfio-pci に割り当てられていることを dmesg の出力で確認してください:
dmesg | grep -i vfio
... [ 0.456472] VFIO - User Level meta-driver version: 0.3 [ 0.470269] vfio_pci: add [10de:13c2[ffff:ffff]] class 0x000000/00000000 [ 0.483631] vfio_pci: add [10de:0fbb[ffff:ffff]] class 0x000000/00000000 [ 0.496998] vfio_pci: add [8086:8ca0[ffff:ffff]] class 0x000000/00000000 [ 2.420184] vfio-pci 0000:0a:00.0: enabling device (0000 -> 0003) [ 38.590395] vfio_ecap_init: 0000:0a:00.0 hiding ecap 0x1e@0x258 [ 38.590413] vfio_ecap_init: 0000:0a:00.0 hiding ecap 0x19@0x900 [ 38.606881] vfio-pci 0000:0a:00.1: enabling device (0000 -> 0002) [ 38.620241] vfio-pci 0000:00:1b.0: enabling device (0000 -> 0002) ...
デバイス id と lspci -nn
の出力を相互に見比べてください。
pci-stub
カーネルが vfio-pci をサポートしていなくても、代わりに pci-stub モジュールが使えます。
pci-stub
を /etc/mkinitcpio.conf
に追加:
/etc/mkinitcpio.conf
MODULES="... pci-stub ..."
上記で上手くいかない場合、/etc/modules-load.d/
にも追加してみてください:
# echo pci-stub > /etc/modules-load.d/vfio.conf
PCI デバイス ID をカーネルコマンドラインに追加:
/etc/mkinitcpio.conf
... GRUB_CMDLINE_LINUX_DEFAULT="... pci-stub.ids=1002:6719,1002:aa80,10de:0392 ..." ...
グラフィックカードの音声が別の PCI デバイスになっている場合、それも追加してください:
pci-stub.ids=1002:6719,1002:aa80
grub の設定をリロード:
# grub-mkconfig -o /boot/grub/grub.cfg
デバイスが pci-stub に割り当てられていることを dmesg の出力で確認:
dmesg | grep pci-stub
... [ 2.390128] pci-stub: add 1002:6719 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000 [ 2.390143] pci-stub 0000:01:00.0: claimed by stub [ 2.390150] pci-stub: add 1002:AA80 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000 [ 2.390159] pci-stub 0000:01:00.1: claimed by stub [ 2.390150] pci-stub: add 1002:0392 sub=FFFFFFFF:FFFFFFFF cls=00000000/00000000 [ 2.390159] pci-stub 0000:04:00.0: claimed by stub ...
モジュールのブラックリスト
あるいは、ホスト側ではパススルーする PCI デバイスのドライバーが必要ない場合 (ホストと VM で使用する GPU のメーカーが異なる場合など)、AMD の GPU なら radeon
や fglrx
を、NVIDIA の GPU では nouveau
や nvidia
を /etc/modprobe.d/blacklist.conf
からブラックリストに指定できます。
例えば、オープンソースの radeon モジュールをブラックリストに入れるには:
/etc/modprobe.d/modprobe.conf
blacklist radeon
VFIO に接続
カードを vfio に接続する方法はたくさんあります:
- firewing1 ウェブページ。grub2-mkconfig の後の部分を見てください。
IOMMU グループ
完全な IOMMU グループだけがゲスト VM に付加されます。PCI デバイスが割り当てられているグループを確認するには:
# find /sys/kernel/iommu_groups/ -type l
/sys/kernel/iommu_groups/0/devices/0000:00:00.0 /sys/kernel/iommu_groups/1/devices/0000:00:01.0 /sys/kernel/iommu_groups/1/devices/0000:01:00.0 /sys/kernel/iommu_groups/1/devices/0000:01:00.1 /sys/kernel/iommu_groups/2/devices/0000:00:02.0 /sys/kernel/iommu_groups/3/devices/0000:00:16.0 /sys/kernel/iommu_groups/4/devices/0000:00:1a.0 /sys/kernel/iommu_groups/5/devices/0000:00:1b.0 /sys/kernel/iommu_groups/6/devices/0000:00:1c.0 /sys/kernel/iommu_groups/7/devices/0000:00:1c.5 /sys/kernel/iommu_groups/8/devices/0000:00:1c.6 /sys/kernel/iommu_groups/9/devices/0000:00:1c.7 /sys/kernel/iommu_groups/9/devices/0000:05:00.0 /sys/kernel/iommu_groups/10/devices/0000:00:1d.0 /sys/kernel/iommu_groups/11/devices/0000:00:1f.0 /sys/kernel/iommu_groups/11/devices/0000:00:1f.2 /sys/kernel/iommu_groups/11/devices/0000:00:1f.3 /sys/kernel/iommu_groups/12/devices/0000:02:00.0 /sys/kernel/iommu_groups/12/devices/0000:02:00.1 /sys/kernel/iommu_groups/13/devices/0000:03:00.0 /sys/kernel/iommu_groups/14/devices/0000:04:00.0
ACS Override パッチ
パススルーしたくない PCI デバイスもグループに入ってしまっている場合、Alex Williamson の ACS override パッチを使うことでデバイスを分離できます。その場合は 危険性 を承知してください。
パッチが適用されたカーネルが必要になります。linux-vfioAUR パッケージでカーネルをインストールするのが一番簡単です。
さらに、ACS override パッチはカーネルのコマンドラインオプションで有効にしなければなりません。パッチファイルは以下のドキュメントを追加します:
pcie_acs_override = [PCIE] Override missing PCIe ACS support for: downstream All downstream ports - full ACS capabilties multifunction All multifunction devices - multifunction ACS subset id:nnnn:nnnn Specfic device - full ACS capabilities Specified as vid:did (vendor/device ID) in hex
通常は pcie_acs_override=downstream
オプションで上手くいきます。
インストールと設定が終わったら、ブートローダーのカーネルパラメータを再設定して pcie_acs_override=
オプションが有効になった状態で新しいカーネルをロードするようにしてください。
QEMU のパーミッション
QEMU にハードウェアにアクセスする権限を与えてください (より安全な方法もあるかもしれません):
/etc/libvirt/qemu.conf
... user = "root" group = "root" clear_emulator_capabilities = 0
QEMU は VFIO ファイルの権限も必要とします。/dev/vfio
内の番号付きファイルを全て記述してください:
# ls -1 /dev/vfio
/etc/libvirt/qemu.conf
... cgroup_device_acl = [ "/dev/null", "/dev/full", "/dev/zero", "/dev/random", "/dev/urandom", "/dev/ptmx", "/dev/kvm", "/dev/kqemu", "/dev/rtc","/dev/hpet", "/dev/vfio/vfio", "/dev/vfio/1" ] ...
参照: firewing1 のウェブページ。
QEMU のコマンド
VGA パススルーを使用して QEMU を実行するコマンドの例:
cp /usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd /tmp/my_vars.fd qemu-system-x86_64 \ -enable-kvm \ -m 2048 \ -cpu host,kvm=off \ -vga none \ -device vfio-pci,host=01:00.0 \ -drive if=pflash,format=raw,readonly,file=/usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd \ -drive if=pflash,format=raw,file=/tmp/my_vars.fd
-enable KVM
- システムの KVM を有効にして、AMD-VI/VT-D を使ってハードウェアの仮想化を行う。
-m [number]
- VM が使用するメモリの量を設定。
-cpu host, kvm=off
- ホストの CPU をエミュレートする。kvm=off
を使用すると NVIDIA のカードがハイパーバイザーを検知できなくなってエラーを吐いて終了します。
-vga none
- QEMU に組み込まれているグラフィックカードのエミュレーションを無効化。
-device vfio-pci,host=01:00.0 \
- VGA パススルーするグラフィックカードの PCI アドレス。
-drive if=flash,format=raw,readonly,file=/usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd
- BIOS のアドレス。
他のコマンドについては QEMU を見てください。
OVMF を使用する VM を作成・設定
virsh を使って VM を以下のように編集:
<domain type='kvm'>
<os> <loader readonly='yes' type='pflash'>/usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd</loader> <nvram template='/usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd'/> </os>
<hyperv> <relaxed state='off'/> <vapic state='off'/> <spinlocks state='off'/> </hyperv>
<features> <kvm> <hidden state='on'/> </kvm> </features>
<clock> <timer name='hypervclock' present='no'/> </clock>
Alex Williamson's blog - using virt-manager のガイドでもいいですが、virt-manager から UEFI が認識されるようにするため、QEMU に ovmf のアドレスを指定する必要があります。/etc/libvirt/qemu.conf
を編集して末尾に以下を追加:
nvram = [ "/usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd:/usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd", ]
それから libvirtd を再起動:
# systemctl restart libvirtd.service
これで OK です。
libvirtd を使用しない QEMU (CLI ベース) の完全な例
以下のスクリプトは Samba と Synergy を起動して、仮想マシンを実行し、仮想マシンがシャットダウンした後に全てを終了します。この方法では libvirtd を実行・設定する必要がありませんが、確認は取れていません。
#!/bin/bash echo "Starting Samba" sudo systemctl start smbd.service sudo systemctl start nmbd.service echo "Starting Synergy" /usr/bin/synergys --daemon --config /etc/synergy.conf # This is probably not neccesary, except when updating the OVMF bios # echo "Removing old OVMF variables" # rm -v ./Windows_ovmf_vars_x64.bin # echo "Copying new OVMF variables" # cp -v /usr/share/ovmf/x64/ovmf_vars_x64.bin ./Windows_ovmf_vars_x64.bin echo "Exporting PulseAudio driver" export QEMU_AUDIO_DRV="pa" echo "Starting VM" sudo \ qemu-system-x86_64 \ -serial none \ -parallel none \ -nodefaults \ -nodefconfig \ -enable-kvm \ -name Windows \ -cpu host,kvm=off,check \ -smp sockets=1,cores=4,threads=2 \ -m 12288 \ -soundhw hda \ -device ich9-usb-uhci3,id=uhci \ -device usb-ehci,id=ehci \ -device nec-usb-xhci,id=xhci \ -drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/ovmf_code_x64.bin \ -drive if=pflash,format=raw,file=./Windows_ovmf_vars_x64.bin \ -rtc base=localtime \ -boot order=c \ -net nic,vlan=0,macaddr=52:54:00:00:00:01,model=virtio,name=net0 \ -net bridge,vlan=0,name=bridge0,br=br0 \ -drive if=virtio,id=drive0,file=./Windows.img,format=raw,cache=none,aio=native \ -nographic \ -device vfio-pci,host=04:00.0,addr=09.0,multifunction=on \ -device vfio-pci,host=04:00.1,addr=09.1 # For GPU sound # add ",multifunction=on" to GPU # -device vfio-pci,host=04:00.1,addr=09.1 # Standard VGA # Remove "-nographic \" and "-device vfio-pci" lines # -vga std # Install # In addination to the steps "Standard VGA", add or change these options # -boot order=d \ # -device ide-cd,drive=drive-cd-disk1,id=cd-disk1,unit=0,bus=ide.0 \ # -drive file=/run/media/melvin/primarydata/Data/OS/Windows_10.img,if=none,id=drive-cd-disk1,media=cdrom \ # -device ide-cd,drive=drive-cd-disk2,id=cd-disk2,unit=0,bus=ide.1 \ # -drive file=/run/media/melvin/primarydata/Data/OS/virtio-win-0.1.109.iso,if=none,id=drive-cd-disk2,media=cdrom \ echo "VM closed" echo "Stopping Synergy" pkill synergys echo "Stopping Samba" sudo systemctl stop smbd.service sudo systemctl stop nmbd.service
上記のサンプルに関する詳細は Red Hat の vfio-users に投稿されたメール を参照。
Synergy で仮想マシンを制御
Synergy を使うことで複数のコンピュータ間で (たとえオペレーションシステムが異なっていても) 簡単にマウスやキーボードを共有することができます。ユースケースとしてはそれぞれ個別のモニターが接続されているコンピュータが机の上に複数ある状況が想定されています。詳しくは Synergy のページを見てください。
Synergy を使って VM を制御したい場合、まず synergy パッケージをインストールしてください。
また、Synergy サーバーはホストで動作させ、サーバーからデバイスにアクセスできるようにする必要があるため、キーボードやマウスは仮想マシンにパススルーしません。
synergy のサーバー設定を作成:
/etc/synergy.conf
# Example config section: screens vm: halfDuplexCapsLock = false halfDuplexNumLock = false halfDuplexScrollLock = false xtestIsXineramaUnaware = false switchCorners = none switchCornerSize = 0 host: halfDuplexCapsLock = false halfDuplexNumLock = false halfDuplexScrollLock = false xtestIsXineramaUnaware = false switchCorners = none switchCornerSize = 0 end section: aliases vm: 10.0.2.15 # default for vm host: 10.0.2.2 # default for host end section: links vm: right = host host: left = vm end section: options relativeMouseMoves = false screenSaverSync = true win32KeepForeground = false switchCorners = none switchCornerSize = 0 end
vm
と host
は仮想マシンとホスト OS のホストネームに置き換えてください。
Alt Gr キーが上手く動作しない場合は screens/vm セクションに altgr = alt
を追加してください。
qemu を起動する前、あるいはスタートアップスクリプトで次を実行:
$ /usr/bin/synergys --daemon --config /etc/synergy.conf
それから仮想マシンに synergy をクライアントとしてダウンロード・設定してください。設定は仮想 OS によります。ただし、User Networking Mode (デフォルト) で QEMU を実行する場合、ホストのデフォルト IP は 10.0.2.2
になります。
ゲームを遊んでいてマウスの動きがおかしかったり敏感すぎると感じた場合、options
セクションの relativeMouseMoves = false
という行を relativeMouseMoves = true
に変更して、ゲームをプレイ中は、Scroll Lock
キーを押してマウスを VM にロックしてください。
オペレーティングシステム
オペレーティングシステムによっては、ある時点で起動しなくなってしまうことがあります。この問題を回避するには、-vga none
を -vga qxl
に置き換えて、オペレーティングシステムをインストールしてから、デバイスマネージャを開いてグラフィックカードの PCI デバイス ID が実際の GPU と同じであることを確認し、グラフィックカードのドライバーをインストールしてから、-vga none
に戻してください。
Nvidia の GeForce Experience を機能させる
GeForce Experience からサポートされていない CPU が存在するとエラーが吐かれて、ゲームの最適化などの機能が機能しない場合、KVM モジュールに ignore_msrs=1
オプションを指定して実装されていない MSR へのアクセスを無視することで問題は解決します:
/etc/modprobe.d/kvm.conf
... options kvm ignore_msrs=1 ...