windows on ArchLinuxで無線フルトラする

# windows on ArchLinuxで無線フルトラする
この記事は[ Linux Advent Calendar 2020](https://qiita.com/advent-calendar/2020/linux)の13日目の記事です。
この記事を書いてる人はAdvent Calendar初参戦どころかQiitaに記事を書くのも初めてです。よろしくおねがいします。

## 2022/08/03追記
VRC公式のドキュメントに[VM上で動かしたときにEACを回避する方法](https://docs.vrchat.com/docs/using-vrchat-in-a-virtual-machine)が掲載されました。運営的にはごく一部のユーザー向けで直接VM上のVRCはサポートしてないけれど動かす方法は調査したし掲載するね的なスタンスです。何にせよ、VM上での動作は下の追記で書いた利用規約には抵触していないと考えられるので、私が試して成功した方法を載せておきます。これは、私の環境では公式で書かれているCPUIDの偽装でVMが立ち上がらない&hyper-vのパススルーではVMは立ち上がるもののVRCにVM判定で弾かれたためです。

### VM上でEACのVM判定を回避しつつVRChatを動作させる
どうもEACはマザボ情報を元にVM判定をしているようです。
KVMではこの情報を上書きできるのでこれを使っていきます。(macはこの方法を使えない)

1. ホスト側で`sudo dmidecode`を実行する。
2. 以下の情報を控える
    * BIOS Informationより
        * Vendor
        * Version
        * Release Date
     * System Informationより
         * Manufacturer
         * Producct Name
         * Version
         * Serial Number
3. virt-managerのXML編集画面なり`virsh edit <VM name>`で設定ファイルを開いて以下を追記
    * your infoは2.で表示された各項目の出力におきかえてください
```xml
<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>
```
4. powershellで`msinfo32`を実行して表示される情報が3.で設定した情報と一致しているか確認する。

<details><summary>過去の追記</summary><div>
## 2022/07/29追記
KVMを用いて仮想化をしている場合には仮想環境上で実行する方法が存在します。(私の環境でも検証済み)
しかし、VRChatがVM上での実行を許可していないこと、利用規約に以下の文言(セキュリティをバイパスするな)があることからBan対象になってもおかしくありません。ですので、具体的な方法について記載しません。
もし、VM上でVRCを実行したいという要望がありましたら[こちらのcanny](https://feedback.vrchat.com/open-beta/p/1212-please-dont-block-vms)に投票して、運営に正式に対応してもらったほうがいいと思います。

```
[利用規約より抜粋]
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](https://feedback.vrchat.com/open-beta/p/1212-please-dont-block-vms)に投票することをおすすめします。もしかしたら、運営が対応するかもしれません。~~
~~また、Linuxでwindowsのプログラムを実行するProton上の実行は問題ないです。(手元で検証済み)~~
~~しかし、Proton上ではオーバーレイ系のソフトやOVR系のソフトは軒並み使えない+おそらく最も普及しているであろうQuest/Quest2はLinuxをネイティブにサポートしていないので、Apexなどと同じようにVMでの実行を許可してくれるとうれしいなぁというところです。~~
</div></details>

## はじめに
私は今年7月から[VRChat](https://hello.vrchat.com/)で生活しています。ヘッドマウントディスプレイ(以下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は絶対必要。そんなにインスコしたくないなら仮想環境で隔離すればいいじゃないと思ったのでやっていきます。

<details><summary>ほぼポエム</summary><div>
## どう実現するのか
KVMとQEMUと使用すとハードウェアレベルで仮想化が行われるためネイティブに近い仮想環境を得ることができます。また、Open Virtual Machine Firmware(OVMF)を使用することでPCIに刺さっているGPUを仮想環境上のOSにパススルーできるためグラフィックについてもネイティブと同程度の性能を得られます。
実際、私はこの環境でVRをしていますが特に問題があるとは感じていません。


## どうして実機windowsを使わないのか
導入は結構めんどくさいのでデュアルブートでいいと思います。ストレージもネイティブレベルの性能を出そうとすると別途用意するほうがいいです(私は別ストレージ上に立てています)し、GPUも余分に必要、スタンバイ状態の消費電力は増えます。EACの制限にも引っかかりません。
複数台持つのはスペース的に厳しいとか、Linuxの操作性(タイリングWMとかターミナル操作)になれてしまってマウス操作を強要されるwindowsが嫌いとかも色々ありますが、私の場合ぶっちゃけ自己満足です。
異種のGPU持っててVMに割り当て直すのがやりやすいとか、いちいちキーボード切り替えるのめんどくさいとか、何もしてないのにクラッシュしやがる(実際はなんの操作が問題だったのかわかりにくい)こととかありますが、多分自己満足です。
</div></details>

## 前提条件
基本的にVRChatが快適にできるPC+$\alpha$くらいの性能で考えてください。最低限は動かせるレベルの最低限です。

|項目|推奨|最低限|
|:-:|:-:|:-:|
|ホストOS [^1]|ArchLinux|x|
|ゲストOS [^2]|windows10|x|
|CPU|6C12T|4C4T(かなり厳しい)|
|RAM|32GB以上|12GB以上|
|GPU1[^3]|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](https://www.vrdesktop.net/)を使います。             
仮想環境上で使う関係でUSB接続の[Oculus Link](https://support.oculus.com/444256562873335/?locale=ja_JP)はかなり不安定です。    
VirtualDesktopは無線でQuest/Quest2と接続するためI/Oデバイス周りでネックになることはありません

[^1]: Ubuntuでもできるようですが私がしたこと無いので取り上げません。prime-selectでできるとかなんとか。
[^2]: 正規のライセンスを買ってください。
[^3]: VRChatが快適にできるGPU条件です。ここは金銭が許す限り強いGPUを搭載してください。最近はRTX3070がオススメです。

## GPUドライバーとXorgをインストールする
* NVIDIA+intel(第4世代まで)の場合

```console
pacman -S xorg-server nvidia mesa vulkan-intel libva-intel-driver
yay -S prime-manager
```
* NVIDIA+intel(第5世代以降)の場合

```console
pacman -S xorg-server nvidia mesa vulkan-intel intel-media-driver
yay -S prime-manager
```

* NVIDIAx2の場合

```console
pacman -S xorg-server nvidia nvidia-utils
```

## NVIDIAドライバーを有効化する

```shell:/etc/mkinitcpio.conf
...
MODULES=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)
...
```
編集したら変更を反映する

```console:root
mkinitcpio -P
```

## 仮想環境用のツールをインストールする
* iptablesを使用している場合       

```console
pacman -S qemu libvirt ovmf ebtables dnsmasq bridge-utils virt-manager
```
* nftablesを使ってる場合

```console
pacman -S qemu libvirt ovmf iptables-nft dnsmasq bridge-utils virt-manager
```

## IOMMUを有効にする
有効化したら一度再起動する

* Intel CPUの場合

```shell:/etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="... intel_iommu=on iommu=pt ..."
```

* AMD CPUの場合

```shell:/etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="... amd_iommu=on iommu=pt ..."
```

* 確認は以下のコマンドを叩いて何か出力されれば有効化されている

```console
dmesg|grep -e DMAR -e IOMMU
```

## IOMMUグループを確認する
PCIeスロットはCPU由来のものとPCH由来のものがあります。PCHはPCIeだけでなくUSBやSATAも管理しています。
そのため、PCH由来のGPUをパススルーしようとするとPCHが管理している他のデバイスも一緒にパススルーしなければなりません。
これはホスト側に影響が大きいため通常、隔離されているCPU由来のPCIeスロットに刺さっているGPUをパススルーします。
ここでは、IOMMUグループを表示してどのデバイスがどのグループで管理されているのかを調べ、隔離されているGPUを探します。
特にNVIDIAx2で構成されている場合は要確認です。

* シェルスクリプトを作成

```shell:checkgroup.sh
#!/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;
```

* 実行

```console
bash checkgroup.sh
```
* 出力の例    
IOMMU Group No PCIデバイスID デバイス区分: デバイスの詳細みたいな感じで出力されます。    
この中からGPUのみのIOMMUグループを探し、そのグループに存在するPCIデバイスIDを覚えておいてください。後で使います。

```console
...
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がロードされる必要があるので下のように各種ドライバーよりも前に記載してください

```shell:/etc/mkinitcpio.conf
...
MODULES=(vfio_pci vfio vfio_iommu_type1 vfio_virqfd nvidia nvidia_modeset nvidia_uvm nvidia_drm)
...
HOOKS=(... modconf ...)
...
```
変更を反映

```console:root
mkinitcpio -P
```

## (NVIDIAx2構成のみ)GPUを分離するスクリプトの作成
archwikiでは`/etc/modprove.d/`以下にファイルを作成しPC起動時に対象のGPUを分離する手法が取られています。
しかしこれでは対象GPUは常に分離されるためホストPCで使用できなくなります。
分離自体はカーネルパラメータに記載することでできるので簡単なスクリプトを作成し、機会に応じて分離できるようにします。
ここでは`grub`を対象とし、スクリプトを実行するとgrubファイルを編集した後、grubを再構成します。

```shell:swichpci.sh
#! /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から分離する(次回起動時)

```console:root
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モードに設定します。

```shell:/etc/optimus-manager/optimus-manager.conf
...
startup_mode=hybrid
...
```

## (異種混合のみ)GPUを動的に分離するスクリプトの作成
windowsのNVIDIAドライバーにはoptimusと呼ばれる内蔵GPUとNVIDIA GPUの両方を搭載するゲーミングPCの消費電力を低くするために使用するGPUを動的に変更する機能があります。
Linux向けのNVIDIAドライバーにも搭載されていますが動的な変更は公式にはサポートされていません。
しかしながら、ArchLinuxではAURリポジトリにある[prime-manager](optimus-manager)を使用することでセッションのログアウトのみでGPUを切り替えることができるようになります。
残念ながら私の環境ではXorgは正常に動くのですが、動画を再生しようとするとカクカクで見れないです。
解決策を知っていたらぜひ教えてください。


```shell:switchmode.sh
#! /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`でマウントされます。

```console:noroot
bash switchmode.sh
> Hybrid to Intel
or
> Intel to Hybrid
```


## 仮想ブリッジを作成する
`virt-manager`のデフォルトのネットワークではゲストOSから外部にアクセスはできますが外部からゲストOSにはアクセスできません。
そこで仮想ブリッジを作成することで外部へゲストOSを公開します。
ここでは`systemd-networkd`での作成のみ紹介します。

* 仮想ブリッジの作成

```shell:/etc/systemd/network/br0.netdev
[NetDev]
Name=br0
Kind=bridge
```

* 仮想ブリッジのネットワーク設定

```shell:/etc/systemd/network/10-br0.network
[Match]
Name=br0
[Network]
DHCP=ipv4
```

* 物理NICを仮想ブリッジに接続する

```shell:/etc/systemd/networkd/20-wired.network
[Match]
Name=enp*
[Network]
Bridge=br0
```

## 仮想環境にwindowsをインストールする
ここまでの設定で疲弊していると思いますがいよいよwindowsをインストールしていきます。
ここでは`libvirt`のフロントエンドである`virt-manager`を使用します。
また、仮想環境上のwindowsの性能向上のために`virtio`を使用します。
### windowsのインストールisoとvirtioのwindowsドライバーisoをダウンロードする
* windowsのインストールisoは[ここ](https://www.microsoft.com/ja-jp/software-download/windows10ISO)
* virtioのwindowsドライバーは[ここのDirect downloads](https://docs.fedoraproject.org/en-US/quick-docs/creating-windows-virtual-machines-using-virtio-drivers/index.html)(stableでいいと思います)

### virt-managerでwindows環境の作成
##### 1.1 作成ウィザードに沿ってインストール作業を進める   
前提条件に沿ってCPUは4コア以上(できれば総スレッドの半分)、RAMは8GB以上与えてください。
ステップ5で必ず`Customize configuration before install`にチェックを入れてください
![2020-12-13_17-38_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/a32a05ed-e48c-1522-66dc-f8229e6f9fca.jpeg)

`Customize configuration before install`にチェック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/451de034-a8cc-d7e4-df08-e3011e88a9fd.png)

##### 1.2 仮想環境を調節する
前の項でFinishを押すと下の画像のような設定画面が開きます。
最終的に下の画像のようになればいいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/aaec313d-9ca6-ffc7-21b0-06e44c0e743c.png)

設定するのは以下のとおりです。

* 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を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/b0fedd61-70d2-8a9b-13ba-ff7543858e2d.png)
![2020-12-13_17-58.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/b63870d7-ef62-5dee-3b8b-e491c7c4e278.jpeg)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/7bbf2a11-1e98-5c8b-c63c-b1c4cb53dfe4.png)


##### 2. windowsをインストールする
起動すると下のような画面が出てきますがここで"exit"と入力します。
![2020-12-13_18-09.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/c6628406-ab47-1f84-fb94-e3fec52685d6.jpeg)
BIOS画面が出てくるのでBoot ManagerからwindowsのインストールisoのCDを指定し、数回Enterを押すとwindowsのインストーラーが起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/969ff8b9-3a69-9cd4-b216-2633dfe40743.png)

カスタムを選択し
![2020-12-13_18-21.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/ce31a5e5-bd11-b443-6127-7b94fe1e42eb.jpeg)
ドライバーの読み込みからwindows10のvirtioドライバーを選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/a9fe4963-64eb-0a51-a3ff-4abd33cb6e31.png)
![2020-12-13_18-22.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/ca1014da-4b38-dd1e-9340-78b86153d914.jpeg)

すると、インストール場所にストレージが追加されているので選択して後はインストーラーの指示に従ってインストールを進めていきます。
このときネットワークに接続ができないと言われますが、接続しないを選択して続行してください。

##### 3. windowsにvirtioのドライバーを当てる
設定->詳細情報->デバイスマネージャーのほかのデバイスにある全てのデバイスにvirtioドライバーをあてます。
対象デバイスを右クリック->ドライバーの更新->手動更新->virtioドライバーのCDを選択を繰り返します。
ドライバーが入るとネットワークに接続できるようになっているはずです。
設定からMicrosoftアカウントにログインしたらここで一旦VMをシャットダウンします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/602424e3-a957-f798-2124-2ffa23823018.png)

## VMにGPUをパススルーする
コンシューマ向けGPUは基本的に仮想化をサポートしていません。
そこでベンダーIDを偽装することでwindowsがパススルーされたGPUを認識できるようにします。

##### 1. GPUがホストPCにバインドされていないか確認する
仮にホストPCにバインドされた状態でGPUパススルーしようとするとカーネルパニックが発生し、強制終了するしかなくなります。
必ず確認してください

* NVIDIAx2の場合: 先に作っておいたスクリプトを実行、再起動した後に`nvidia-smi`を実行して対象GPUが表示されていない
* 異種混合の場合: 先に作っておいたスクリプトを実行、セッションからログアウトした後に、`nvidia-smi`を実行してプロセスに何も表示されていない

##### 2. VMのConfigを編集する
VMのOverview->XMLを選択して下のように追記していきます。

```xml:virt-manager
...
    <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はそのままにしておいてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/a9f6ab92-48db-d6d9-e2e0-4470fcd6a6e0.png)

##### 4. 仮想マシンのチューニング

- VMのタイマー管理
    - タイマー管理に`hpet`を使用する
    - `rtx`+`pit`ではCPUが非常にボトルネックになる
    - [参考: High KVM/QEMU CPU utilization when Windows 10 guest is idle](https://www.reddit.com/r/VFIO/comments/80p1q7/high_kvmqemu_cpu_utilization_when_windows_10/)

```xml
  <!-- 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](https://github.com/bryansteiner/gpu-passthrough-tutorial)

```xml
<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](https://chocolatey.org/)がオススメです。)

* [NVIDIA Driver](https://www.nvidia.com/ja-jp/geforce/geforce-experience/)
* [Steam](https://store.steampowered.com/)
* [Oculus setup](https://www.oculus.com/setup/)
* [Virtual Desktop Streamer](https://www.vrdesktop.net/)
* [OpenVR-AdvancedSettings](https://github.com/OpenVR-Advanced-Settings/OpenVR-AdvancedSettings)

<details><summary>[2021/9/24追記] パッチを当てなくても良くなりました</summary><div>
VirtualDesktopでSteamを実行するには別の作業が必要になります。
[ここ](https://tona.hatenablog.jp/entry/2020/09/21/205704)が詳しく解説してくれているので見てセットアップしてください。
VirtualDesktopにパッチを当てるのに必要な[sidequest](https://sidequestvr.com/)はクロスプラットフォームなのでArchLinuxでも以下でインストールすれば使えます。

```console
yay -S sidequest-bin
```

また、仮想ブリッジを用いてVMを構築したためQuest/Quest2のVitualDesktopアプリからwindowsが見えているはずです。
もし見えていなかったらネットワーク周りを確認してください。
</div></details>

## Vive Trackerとベースステーションを設定する
ここまで来たら後少しです!
設定の前にリダイレクトできるUSBの数を増やします。
VMの設定->Add HardwareからUSB Redirectorを選択します。最低5つあれば足ります。(トラッカー用ドングルx3+入力装置x2)
もしくはドングルを直接VMに追加してください。(Add Hardware->USB Host Device)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/fd86405a-5152-f50d-754e-1306382c3d72.png)
Vive Trackerとベースステーションのファームウェアを更新してSteamで使用できるように設定していきます。
##### 1. ベースステーションとトラッカーとVMをペアリングする
ペアリングする前にトラッカーに付属のドングルを全てVMにリダイレクトしてください。
後は[ここ](https://tona.hatenablog.jp/entry/2019/12/11/231111)に書いてある方法でペアリングができると思います。

##### 2. ベースステーションのファームウェアを更新する
[ここ](https://www.vive.com/jp/support/vive/category_howto/updating-the-base-stations-via-cable-connection.html)を参考にファームウェアを更新してください。
上記リンクの4の項目はホストOSにマウント->VMにUSBリダイレクトと読み替えてください。
(USBリダイレクトはVMの設定->Virtual Machine->USB Redirectから可能です。)

##### 3. Vive Trackerのファームウェアを更新する
先に付属のドングルをVMにリダイレクトしてSteam上でトラッカーを認識させてください。
ファームウェアの更新が必要であればドングルのリダイレクトを解除し</div></details>て[ここ](https://www.vive.com/jp/support/wireless-tracker/category_howto/updating-the-firmware.html)参考に更新してください。
上記リンクの4のUSBに接続はVMにUSBリダイレクトと読み替えてください。
また、この方法で更新をするとSteam上では「失敗しました」と表示されます。
しかし、再度ドングルをVMにリダイレクトしてトラッカーを接続すると正常に動作し、ファームウェアの更新も通知されません。
おそらく、更新はできているとは思いますが保証はできません。

## VRChatにログインしてフルトラする
お疲れ様でした!
これで全ての作業は終了です。Virtual DesktopからVRChatを起動してSteam上でトラッカーが認識されていればフルトラッキングできるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178139/38b7149f-fa68-a1be-7f61-b2c745e7e7a8.png)

## おわりに
今回はVRを実行しかつフルトラッキングできる仮想環境windowsを作成してみました。
見ていただいた方はお分かりいただけたでしょうが作業量が半端じゃないです。
実際にはVMを作るだけでなく、後半に割愛したVirtual Desktopの設定やトラッカーの設定が作業量に追加されます。
いっそのことデュアルブートするか、もう一台VR専用のPCを買えばいいと思います。
本当に趣味の領域を超えないような気はしますが誰かが同じようなことをするときの参考になれば嬉しいです。

余談ですが、[HTC vive](https://www.vive.com/jp/)や[Valve Index](https://steamvr.jp/)だとオーバーレイアプリが使えないなどの制約はありますがフルトラも完全にLinux上でできるようです。([ソース](https://www.reddit.com/r/ValveIndex/comments/e2wkud/valve_index_on_linux_and_first_experiences/))
SteamVR自体はクロスプラットフォームで有線のHMDは実質的にただの画面だからできる芸当です。
それでも、ケーブルレスな無線フルトラは快適ですし現状それが可能なのはQuest/Quest2のみなのが辛いところです...。

## 参考サイト
* [QEMU](https://wiki.archlinux.jp/index.php/QEMU)
* [libvirt](https://wiki.archlinux.jp/index.php/Libvirt)
* [OVMF による PCI パススルー](https://wiki.archlinux.jp/index.php/OVMF_%E3%81%AB%E3%82%88%E3%82%8B_PCI_%E3%83%91%E3%82%B9%E3%82%B9%E3%83%AB%E3%83%BC)
* [Linux, AMD Ryzen 3900X, X570, NVIDIA GTX 1060, AMD 5700XT, Looking Glass, PCI Passthrough and Windows 10](https://www.tauceti.blog/post/linux-amd-x570-nvidia-gpu-pci-passthrough-1-the-hardware/)
* [【Linux Mint】【QEMU/KVM】KVMによる仮想化Win10へのGPUパススルー設定](https://engetu21.hatenablog.com/entry/2018/04/03/003642)

## Quest/Quest2でのフルトラで参考になりそうなサイト
* [Oculus Questで無線フルトラをする - 簡易設定編](https://tona.hatenablog.jp/entry/2019/12/11/231111)
* [Virtual Desktopを最適な設定にする - 各種設定について](https://tona.hatenablog.jp/entry/2020/09/21/205704)
* [sidequest](https://sidequestvr.com/)