OVMF による PCI パススルー

提供: ArchWiki
2016年3月3日 (木) 15:24時点におけるKusakata (トーク | 投稿記録)による版 (→‎pci-stub: 同期)
ナビゲーションに移動 検索に移動

こちらは QEMU で PCI VGA パススルーを行う方法のガイドです。カーネル 3.9 から QEMU に変更があり、グラフィックカードをパススルーすることが可能になりました。ゲームなどのグラフィック負担が重い処理を実行するときに、VM からネイティブのグラフィック性能を発揮できます。グラフィックカードをパススルーするには、2つのグラフィックカードが必要になります。片方はホストに、もう片方は VM に割り当てられます。グラフィックカードといっても、ホスト側では内蔵のグラフィックを使うことができます。また、プロセッサとマザーボードが AMD-VI/VT-D に対応していなければなりません。

インストール

qemurpmextractインストールしてください。カーネルにパッチを適用する必要がある場合は 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
ノート: 標準のカーネルを使っていない場合、上記コマンドの "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 なら radeonfglrx を、NVIDIA の GPU では nouveaunvidia/etc/modprobe.d/blacklist.conf からブラックリストに指定できます。

例えば、オープンソースの radeon モジュールをブラックリストに入れるには:

/etc/modprobe.d/modprobe.conf
blacklist radeon

VFIO に接続

カードを vfio に接続する方法はたくさんあります:

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

vmhost は仮想マシンとホスト 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
...
警告: 未知の MSR のアクセスを無視すると、VM 内の他のソフトウェアや他の VM が動作しなくなる可能性があります。

参照