windows on ArchLinuxで無線フルトラする
この記事は Linux Advent Calendar 2020の13日目の記事です。
この記事を書いてる人はAdvent Calendar初参戦どころかQiitaに記事を書くのも初めてです。よろしくおねがいします。
2022/08/03追記
VRC公式のドキュメントにVM上で動かしたときにEACを回避する方法が掲載されました。運営的にはごく一部のユーザー向けで直接VM上のVRCはサポートしてないけれど動かす方法は調査したし掲載するね的なスタンスです。何にせよ、VM上での動作は下の追記で書いた利用規約には抵触していないと考えられるので、私が試して成功した方法を載せておきます。これは、私の環境では公式で書かれているCPUIDの偽装でVMが立ち上がらない&hyper-vのパススルーではVMは立ち上がるもののVRCにVM判定で弾かれたためです。
VM上でEACのVM判定を回避しつつVRChatを動作させる
どうもEACはマザボ情報を元にVM判定をしているようです。
KVMではこの情報を上書きできるのでこれを使っていきます。(macはこの方法を使えない)
- ホスト側で
sudo dmidecode
を実行する。 - 以下の情報を控える
- BIOS Informationより
- Vendor
- Version
- Release Date
- System Informationより
- Manufacturer
- Producct Name
- Version
- Serial Number
- BIOS Informationより
- virt-managerのXML編集画面なり
virsh edit <VM name>
で設定ファイルを開いて以下を追記- your infoは2.で表示された各項目の出力におきかえてください
<vcpu>...</vcpu>
<!--追記ココから-->
<sysinfo type='smbios'>
<bios>
<entry name='vendor'>your info</entry>
<entry name='version'>your info</entry>
<entry name='date'>your info</entry>
</bios>
<system>
<entry name='manufacturer'>your info</entry>
<entry name='product'>your info</entry>
<entry name='version'>your info</entry>
<entry name='serial'>your info</entry>
</system>
</sysinfo>
<os>
...
<smbios mode='sysinfo'/>
</os>
- powershellで
msinfo32
を実行して表示される情報が3.で設定した情報と一致しているか確認する。
過去の追記
[利用規約より抜粋]
h. bypass any security or other features of the Platform designed to control how the Platform is used, harvest, or mine User Content from the Platform, or otherwise access or use the Platform in a manner inconsistent with individual human usage;
2022/07/26追記
2022/07/25更新のOpenBetaにて、VRChatはEasy-Anti-Cheat(以下EAC)を導入しました。26日現在、運営は仮想マシン上でEACを実行することを許可していないためこの方法は使えません。2. VMのconfigを編集するにある仮想マシンでないと偽装する方法を使うことで回避できた例があるようですが、環境に依存します。(ゲーミングノートではだめだった。)
EACの導入についての是非はここでは論じませんが、今後もVM上で実行したいという欲求があるならばこちらのcannyに投票することをおすすめします。もしかしたら、運営が対応するかもしれません。
また、Linuxでwindowsのプログラムを実行するProton上の実行は問題ないです。(手元で検証済み)
しかし、Proton上ではオーバーレイ系のソフトやOVR系のソフトは軒並み使えない+おそらく最も普及しているであろうQuest/Quest2はLinuxをネイティブにサポートしていないので、Apexなどと同じようにVMでの実行を許可してくれるとうれしいなぁというところです。
はじめに
私は今年7月からVRChatで生活しています。ヘッドマウントディスプレイ(以下HMD)はOculus Quest2を使用し、vive trackerで全身をトラッキングしてVRCの世界でも全身を動かせる環境を整えるほどにのめり込んでいます。
Oculus Quest/Quest2はOculus LinkやVirtual Desktopを使用することでPCリソースを使用するPCVR化が可能であり、より画質のいいVRC世界を見たり、PCのみの世界に行ったり、また前述のフルトラを実現することができます。
さて、そんな私ですが普段はArchLinux + Qtileという環境を使用しておりwindowsとは全くの無縁です。日常作業に支障はありませんが、一点Quest/Quest2のPCVR化にはwindowsが絶対に必要です。しかし、私は過去ubuntuとデュアルブートをしたことで双方が破壊した経験があるため極力デュアルブートにしたくありません。でもVRCでフルトラしたいし、windowsは絶対必要。そんなにインスコしたくないなら仮想環境で隔離すればいいじゃないと思ったのでやっていきます。
ほぼポエム
どうして実機windowsを使わないのか
導入は結構めんどくさいのでデュアルブートでいいと思います。ストレージもネイティブレベルの性能を出そうとすると別途用意するほうがいいです(私は別ストレージ上に立てています)し、GPUも余分に必要、スタンバイ状態の消費電力は増えます。EACの制限にも引っかかりません。
複数台持つのはスペース的に厳しいとか、Linuxの操作性(タイリングWMとかターミナル操作)になれてしまってマウス操作を強要されるwindowsが嫌いとかも色々ありますが、私の場合ぶっちゃけ自己満足です。
異種のGPU持っててVMに割り当て直すのがやりやすいとか、いちいちキーボード切り替えるのめんどくさいとか、何もしてないのにクラッシュしやがる(実際はなんの操作が問題だったのかわかりにくい)こととかありますが、多分自己満足です。
前提条件
基本的にVRChatが快適にできるPC+$\alpha$くらいの性能で考えてください。最低限は動かせるレベルの最低限です。
項目 | 推奨 | 最低限 |
---|---|---|
ホストOS 1 | ArchLinux | x |
ゲストOS 2 | windows10 | x |
CPU | 6C12T | 4C4T(かなり厳しい) |
RAM | 32GB以上 | 12GB以上 |
GPU13 | NVIDIA RTX2060S以上 | NVIDIA GTX1060 6GB |
GPU2(異種混合の場合) | intel iGPU or AMD Radeon | x |
GPU2(NVIDIAのみの場合) | GeForce 600シリーズ以降 | x |
ストレージ | 512GB以上 | 256GB以上 |
ネットワーク | 有線+5gHz対応のルーター | PCも無線は遅延が大きくなる |
- ネットワークドライバーは
systemd-networkd
を使用します - 私はNVIDIA+intel, NVIDIAx2の構成でしか試したことありませんのでNVIDIA+AMDは書きません
(ドライバーの部分を読み替えれば動くとは思いますが) - PCVR化するソフトはVirtual Desktopを使います。
仮想環境上で使う関係でUSB接続のOculus Linkはかなり不安定です。
VirtualDesktopは無線でQuest/Quest2と接続するためI/Oデバイス周りでネックになることはありません
GPUドライバーとXorgをインストールする
- NVIDIA+intel(第4世代まで)の場合
pacman -S xorg-server nvidia mesa vulkan-intel libva-intel-driver
yay -S prime-manager
- NVIDIA+intel(第5世代以降)の場合
pacman -S xorg-server nvidia mesa vulkan-intel intel-media-driver
yay -S prime-manager
- NVIDIAx2の場合
pacman -S xorg-server nvidia nvidia-utils
NVIDIAドライバーを有効化する
...
MODULES=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)
...
編集したら変更を反映する
mkinitcpio -P
仮想環境用のツールをインストールする
- iptablesを使用している場合
pacman -S qemu libvirt ovmf ebtables dnsmasq bridge-utils virt-manager
- nftablesを使ってる場合
pacman -S qemu libvirt ovmf iptables-nft dnsmasq bridge-utils virt-manager
IOMMUを有効にする
有効化したら一度再起動する
- Intel CPUの場合
GRUB_CMDLINE_LINUX_DEFAULT="... intel_iommu=on iommu=pt ..."
- AMD CPUの場合
GRUB_CMDLINE_LINUX_DEFAULT="... amd_iommu=on iommu=pt ..."
- 確認は以下のコマンドを叩いて何か出力されれば有効化されている
dmesg|grep -e DMAR -e IOMMU
IOMMUグループを確認する
PCIeスロットはCPU由来のものとPCH由来のものがあります。PCHはPCIeだけでなくUSBやSATAも管理しています。
そのため、PCH由来のGPUをパススルーしようとするとPCHが管理している他のデバイスも一緒にパススルーしなければなりません。
これはホスト側に影響が大きいため通常、隔離されているCPU由来のPCIeスロットに刺さっているGPUをパススルーします。
ここでは、IOMMUグループを表示してどのデバイスがどのグループで管理されているのかを調べ、隔離されているGPUを探します。
特にNVIDIAx2で構成されている場合は要確認です。
- シェルスクリプトを作成
#!/usr/bin/env bash
shopt -s nullglob
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*}
printf 'IOMMU Group %s ' "$n"
lspci -nns "${d##*/}"
done;
- 実行
bash checkgroup.sh
- 出力の例
IOMMU Group No PCIデバイスID デバイス区分: デバイスの詳細みたいな感じで出力されます。
この中からGPUのみのIOMMUグループを探し、そのグループに存在するPCIデバイスIDを覚えておいてください。後で使います。
...
IOMMU Group 18 26:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU104 [GeForce RTX 2070 SUPER] [10de:1e84] (rev a1)
IOMMU Group 18 26:00.1 Audio device [0403]: NVIDIA Corporation TU104 HD Audio Controller [10de:10f8] (rev a1)
IOMMU Group 18 26:00.2 USB controller [0c03]: NVIDIA Corporation TU104 USB 3.1 Host Controller [10de:1ad8] (rev a1)
IOMMU Group 18 26:00.3 Serial bus controller [0c80]: NVIDIA Corporation TU104 USB Type-C UCSI Controller [10de:1ad9] (rev a1)
IOMMU Group 19 27:00.0 Non-Essential Instrumentation [1300]: Advanced Micro Devices, Inc. [AMD] Starship/Matisse PCIe Dummy Function [1022:148a]
...
vfio-pciモジュールをロードする
GPUドライバーがロードされるより前にvfioがロードされる必要があるので下のように各種ドライバーよりも前に記載してください
...
MODULES=(vfio_pci vfio vfio_iommu_type1 vfio_virqfd nvidia nvidia_modeset nvidia_uvm nvidia_drm)
...
HOOKS=(... modconf ...)
...
変更を反映
mkinitcpio -P
(NVIDIAx2構成のみ)GPUを分離するスクリプトの作成
archwikiでは/etc/modprove.d/
以下にファイルを作成しPC起動時に対象のGPUを分離する手法が取られています。
しかしこれでは対象GPUは常に分離されるためホストPCで使用できなくなります。
分離自体はカーネルパラメータに記載することでできるので簡単なスクリプトを作成し、機会に応じて分離できるようにします。
ここではgrub
を対象とし、スクリプトを実行するとgrubファイルを編集した後、grubを再構成します。
#! /usr/bin/env bash
GRUBFILE=/etc/default/grub
if [ ! $USER = root ]; then
echo "Please run under root user"
exit 1
fi
if [ $(cat $GRUBFILE | grep -w GRUB_CMDLINE_LINUX) = "GRUB_CMDLINE_LINUX=\"\"" ]; then
echo -e "\033[0;31mLinux only to Shared VM\033[0;39m"
sed -i -e "s/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\""上のIOMMUグループの確認したグループに所属する全てのPCIデバイスIDをカンマ区切りで記載する"\"/g" $GRUBFILE
else
echo -e "\033[0;31mShared VM to Linux only\033[0;39m"
sed -i -e "s/GRUB_CMDLINE_LINUX=\"vfio-pci.ids="上のIOMMUグループの確認したグループに所属する全てのPCIデバイスIDをカンマ区切りで記載する"\"/GRUB_CMDLINE_LINUX=\"\"/g" $GRUBFILE
fi
grub-mkconfig -o /boot/grub/grub.cfg
echo "Done!"
- 実行する
to Linux only
でホストPCにバインド、to Shared VM
で対象GPUをホストPCから分離する(次回起動時)
bash ./switchpci.sh
> Shared VM to Linux only
Generating grub configuration file ...
or
> Linux only to Shared VM
Generating grub configuration file ...
(異種混合のみ)GPUの設定
iGPUと外部GPUが混在する場合、iGPUで画面出力するのか外部GPUで出力するのかの設定が必要です。
今回はoptimus-manager
を使用しているため、起動時の初期状態はiGPUで画面出力をし、外部GPUは明示的に呼び出すHybridモードに設定します。
...
startup_mode=hybrid
...
(異種混合のみ)GPUを動的に分離するスクリプトの作成
windowsのNVIDIAドライバーにはoptimusと呼ばれる内蔵GPUとNVIDIA GPUの両方を搭載するゲーミングPCの消費電力を低くするために使用するGPUを動的に変更する機能があります。
Linux向けのNVIDIAドライバーにも搭載されていますが動的な変更は公式にはサポートされていません。
しかしながら、ArchLinuxではAURリポジトリにあるprime-managerを使用することでセッションのログアウトのみでGPUを切り替えることができるようになります。
残念ながら私の環境ではXorgは正常に動くのですが、動画を再生しようとするとカクカクで見れないです。
解決策を知っていたらぜひ教えてください。
#! /usr/bin/env bash
if [ $(optimus-manager --status | grep next | awk '{print $NF}') = "intel" ]; then
echo -e "\033[0;31mIntel to Hybrid\033[0;39m"
optimus-manager --switch hybrid --no-confirm
else
echo -e "\033[0;31mHybrid to Intel\033[0;39m"
optimus-manager --switch intel --no-confirm
fi
- 実行する
Hybrid to Intel
でNVIDIA GPUがアンマウントされます。
Intel to Hybrid
でマウントされます。
bash switchmode.sh
> Hybrid to Intel
or
> Intel to Hybrid
仮想ブリッジを作成する
virt-manager
のデフォルトのネットワークではゲストOSから外部にアクセスはできますが外部からゲストOSにはアクセスできません。
そこで仮想ブリッジを作成することで外部へゲストOSを公開します。
ここではsystemd-networkd
での作成のみ紹介します。
- 仮想ブリッジの作成
[NetDev]
Name=br0
Kind=bridge
- 仮想ブリッジのネットワーク設定
[Match]
Name=br0
[Network]
DHCP=ipv4
- 物理NICを仮想ブリッジに接続する
[Match]
Name=enp*
[Network]
Bridge=br0
仮想環境にwindowsをインストールする
ここまでの設定で疲弊していると思いますがいよいよwindowsをインストールしていきます。
ここではlibvirt
のフロントエンドであるvirt-manager
を使用します。
また、仮想環境上のwindowsの性能向上のためにvirtio
を使用します。
windowsのインストールisoとvirtioのwindowsドライバーisoをダウンロードする
- windowsのインストールisoはここ
- virtioのwindowsドライバーはここのDirect downloads(stableでいいと思います)
virt-managerでwindows環境の作成
1.1 作成ウィザードに沿ってインストール作業を進める
前提条件に沿ってCPUは4コア以上(できれば総スレッドの半分)、RAMは8GB以上与えてください。
ステップ5で必ずCustomize configuration before install
にチェックを入れてください
Customize configuration before install
にチェック
1.2 仮想環境を調節する
前の項でFinishを押すと下の画像のような設定画面が開きます。
最終的に下の画像のようになればいいです。
設定するのは以下のとおりです。
- OverviewのChipsetをQ35、FirmwareをUEFIにする(OVMF_CODE.fdの方でいい)
- CPUsのTopologyのところのCoresを変更する(ウィザードのままだとSocketsに割り当てられwindowsが認識できない)
- NICのNetwork InterfaceをBridge deviceにしてDevice Nameを先に作成したbr0にする
- NICのDevice modelをvirtioに変更
- Disc 1のBus Typeをvirtioに変更
- Add HardwareからStorageを選択、Device typeをCDROMに変更しSelect..から先にダウンロードしたvirtioのwindowsドライバーisoを選択
2. windowsをインストールする
起動すると下のような画面が出てきますがここで"exit"と入力します。
BIOS画面が出てくるのでBoot ManagerからwindowsのインストールisoのCDを指定し、数回Enterを押すとwindowsのインストーラーが起動します。
カスタムを選択し
ドライバーの読み込みからwindows10のvirtioドライバーを選択します
すると、インストール場所にストレージが追加されているので選択して後はインストーラーの指示に従ってインストールを進めていきます。
このときネットワークに接続ができないと言われますが、接続しないを選択して続行してください。
3. windowsにvirtioのドライバーを当てる
設定->詳細情報->デバイスマネージャーのほかのデバイスにある全てのデバイスにvirtioドライバーをあてます。
対象デバイスを右クリック->ドライバーの更新->手動更新->virtioドライバーのCDを選択を繰り返します。
ドライバーが入るとネットワークに接続できるようになっているはずです。
設定からMicrosoftアカウントにログインしたらここで一旦VMをシャットダウンします。
VMにGPUをパススルーする
コンシューマ向けGPUは基本的に仮想化をサポートしていません。
そこでベンダーIDを偽装することでwindowsがパススルーされたGPUを認識できるようにします。
1. GPUがホストPCにバインドされていないか確認する
仮にホストPCにバインドされた状態でGPUパススルーしようとするとカーネルパニックが発生し、強制終了するしかなくなります。
必ず確認してください
- NVIDIAx2の場合: 先に作っておいたスクリプトを実行、再起動した後に
nvidia-smi
を実行して対象GPUが表示されていない - 異種混合の場合: 先に作っておいたスクリプトを実行、セッションからログアウトした後に、
nvidia-smi
を実行してプロセスに何も表示されていない
2. VMのConfigを編集する
VMのOverview->XMLを選択して下のように追記していきます。
...
<hyperv>
...
<vendor_id state="on" value="0123756792CD"/>
</hyperv>
...
3. VMにGPUをマウント
VMの設定からAdd Hardware->PCI Host Deviceで先に確認したGPUを選択してください。
GPU本体だけでなくGPUに付属してるオーディオコントローラーやUSBコントローラーを全てマウントしてください。
今回はSpiceを用いてUSBをゲストOSにマウントするためVideo QXLはそのままにしておいてください。
4. 仮想マシンのチューニング
- VMのタイマー管理
- タイマー管理に
hpet
を使用する -
rtx
+pit
ではCPUが非常にボトルネックになる - 参考: High KVM/QEMU CPU utilization when Windows 10 guest is idle
- タイマー管理に
<!-- before: this config uses over 15% of a host CPU core -->
<clock offset='localtime'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
<timer name='hypervclock' present='yes'/>
</clock>
<!-- after: this config drops to about 3% of a host CPU core -->
<clock offset='localtime'>
<timer name='hpet' present='yes'/>
<timer name='hypervclock' present='yes'/>
</clock>
- CPU Tuning
- VMが使用するCPUを固定する
- 特にRyzenではCCX跨ぎの影響を軽減する
-
cpuset
は使用するCPUによる - 参考: bryansteiner/gpu-passthrough-tutorial
<vcpu placement="static">12</vcpu>
<iothreads>1</iothreads>
<cputune>
<vcpupin vcpu="0" cpuset="6"/>
<vcpupin vcpu="1" cpuset="18"/>
<vcpupin vcpu="2" cpuset="7"/>
<vcpupin vcpu="3" cpuset="19"/>
<vcpupin vcpu="4" cpuset="8"/>
<vcpupin vcpu="5" cpuset="20"/>
<vcpupin vcpu="6" cpuset="9"/>
<vcpupin vcpu="7" cpuset="21"/>
<vcpupin vcpu="8" cpuset="10"/>
<vcpupin vcpu="9" cpuset="22"/>
<vcpupin vcpu="10" cpuset="11"/>
<vcpupin vcpu="11" cpuset="23"/>
<emulatorpin cpuset="0-3"/>
<iothreadpin iothread='1' cpuset='4-5,12-17'/>
</cputune>
パススルーの確認と入力デバイスのマウント
タスクマネージャーを開いてGPUの項目にパススルーしたGPUが表示されていればパススルーは成功です。
VMの操作は2通りあり、virt-managerの画面からマウスとキーボード操作する方法とホストの入力デバイスをリダイレクトでマウントする方法です。
後者はVMをシャットダウンするまでホストOSを操作できません。
USBリダイレクトはVMの設定->Virtual Machine->USB Redirectから可能です。
必要なソフトをダウンロードしてセットアップする
下記のソフトをインストールしてください。インストール方法は他を見てください。
(Linuxのパッケージ管理になれているならchocolateyがオススメです。)
[2021/9/24追記] パッチを当てなくても良くなりました
yay -S sidequest-bin
また、仮想ブリッジを用いてVMを構築したためQuest/Quest2のVitualDesktopアプリからwindowsが見えているはずです。
もし見えていなかったらネットワーク周りを確認してください。
Vive Trackerとベースステーションを設定する
ここまで来たら後少しです!
設定の前にリダイレクトできるUSBの数を増やします。
VMの設定->Add HardwareからUSB Redirectorを選択します。最低5つあれば足ります。(トラッカー用ドングルx3+入力装置x2)
もしくはドングルを直接VMに追加してください。(Add Hardware->USB Host Device)
Vive Trackerとベースステーションのファームウェアを更新してSteamで使用できるように設定していきます。
1. ベースステーションとトラッカーとVMをペアリングする
ペアリングする前にトラッカーに付属のドングルを全てVMにリダイレクトしてください。
後はここに書いてある方法でペアリングができると思います。
2. ベースステーションのファームウェアを更新する
ここを参考にファームウェアを更新してください。
上記リンクの4の項目はホストOSにマウント->VMにUSBリダイレクトと読み替えてください。
(USBリダイレクトはVMの設定->Virtual Machine->USB Redirectから可能です。)
3. Vive Trackerのファームウェアを更新する
先に付属のドングルをVMにリダイレクトしてSteam上でトラッカーを認識させてください。
ファームウェアの更新が必要であればドングルのリダイレクトを解除してここ参考に更新してください。
上記リンクの4のUSBに接続はVMにUSBリダイレクトと読み替えてください。
また、この方法で更新をするとSteam上では「失敗しました」と表示されます。
しかし、再度ドングルをVMにリダイレクトしてトラッカーを接続すると正常に動作し、ファームウェアの更新も通知されません。
おそらく、更新はできているとは思いますが保証はできません。
VRChatにログインしてフルトラする
お疲れ様でした!
これで全ての作業は終了です。Virtual DesktopからVRChatを起動してSteam上でトラッカーが認識されていればフルトラッキングできるはずです。
おわりに
今回はVRを実行しかつフルトラッキングできる仮想環境windowsを作成してみました。
見ていただいた方はお分かりいただけたでしょうが作業量が半端じゃないです。
実際にはVMを作るだけでなく、後半に割愛したVirtual Desktopの設定やトラッカーの設定が作業量に追加されます。
いっそのことデュアルブートするか、もう一台VR専用のPCを買えばいいと思います。
本当に趣味の領域を超えないような気はしますが誰かが同じようなことをするときの参考になれば嬉しいです。
余談ですが、HTC viveやValve Indexだとオーバーレイアプリが使えないなどの制約はありますがフルトラも完全にLinux上でできるようです。(ソース)
SteamVR自体はクロスプラットフォームで有線のHMDは実質的にただの画面だからできる芸当です。
それでも、ケーブルレスな無線フルトラは快適ですし現状それが可能なのはQuest/Quest2のみなのが辛いところです...。
参考サイト
- QEMU
- libvirt
- OVMF による PCI パススルー
- Linux, AMD Ryzen 3900X, X570, NVIDIA GTX 1060, AMD 5700XT, Looking Glass, PCI Passthrough and Windows 10
- 【Linux Mint】【QEMU/KVM】KVMによる仮想化Win10へのGPUパススルー設定