Windows11上でGPU有効な仮想Windows11を動かす

## この記事で行ったこと

Windows 11(10でも)が動作するPC上に仮想マシンでWindowsを動かしたくなることがたまにあると思います。ホストWindowsの環境を汚したくないとき、本来は開発用のマシンだけどちょっとゴニョゴニョしたいときなど。  
特にGPUアクセラレーションを必要とするソフトを仮想マシン上で動かしたくなった場合にはまだ一般ユーザでも簡単に、というわけにはいかないので、Hyper-V上でGPU有効な仮想Windowsを構築する手順を作業ログとして残すことにしました。  
なお、本記事で行う内容はWindowsのデバイスドライバやシステムファイルに手を加えますので、リスクを十分理解のうえ実行される場合は自己責任でお願いします。

---

## GPUを仮想マシンで使用するための技術選択肢

Windows上で仮想マシンにGPUを認識させるための技術はメジャーなところで大きく分けて3つあるようです。

* GPUパススルー (Direct Device Assignment; DDA)
* RemoteFX
* GPU Partitioning (GPU-P)

これらのうち、DDAはホストマシンがWindows Server 2022などのサーバーグレードの製品のみに実装されており、Windows 10/11のような一般ユーザ向けOSでは使用できないようです(実験してみましたが、仮想マシンを起動させることができませんでした)。  
また、RemoteFXは重大なセキュリティ脆弱性が発覚したため現在は使用不能になっているそうです。  
従って、今回はGPU-Pを使用して仮想マシン上からGPUを認識させることにしました。GPU-PはGPUのハードウェア能力を分割して、一部をホストOS用に、一部をゲストOS用に割り当てる機能であるとのことです。

---

## 作業を行った環境のまとめ

今回Windows 11上で仮想Windows 11を構築した環境を以下にまとめます。

### ハードウェア環境

* CPU: AMD Ryzen Threadripper 3990X
* メインメモリ: 256GB
* GPU0: NVIDIA Quadro RTX 5000 (VRAM 16GB)
* GPU1: NVIDIA GeForce RTX 3090 (VRAM 24GB)

### ソフトウェア環境

* ホストOS: Windows 11 Pro Education
* 仮想マシンエンジン: Hyper-V
* ゲストOS: Windows 11 Pro

ライセンスの関係でホストOSがEducation editionのWindows 11ですが、ゲストOSのライセンス料金以外の部分は一般向けのWindows 11 Proと変わらないと思います。  

今回行った環境構築では@Hyper-W氏のQiita記事[「Hyper-VでGPU(3Dアクセラレーション)を利用する方法 Windows 10以降編 (GPU-P)」](https://qiita.com/Hyper-W/items/3a2c7ff1d983deb80070)で紹介されていた方法を元にして、Windows 11/マルチGPU環境用に少し変更を加えたものです。ありがとうございます。  
また、Windows 11から利用可能になったマルチGPU環境でのGPU選択についてはJames Stringer氏の[Easy-GPU-PV](https://github.com/jamesstringerparsec/Easy-GPU-PV)のソースを参考にさせて頂きました。ありがとうございます。

---

## 準備1 ゲストOS用のISOイメージの入手

今回は仮想マシン上で動作させるゲストOSにWindows 11 Proを予定していますので、[Microsoftのサイト](https://www.microsoft.com/ja-jp/software-download/windows11)からWindows 11のISOイメージをダウンロードしてホストOS上のディスクに保存しておきます。

---

## 準備2 UEFIとホストOSの設定確認

マシンを再起動し、UEFIの設定画面で「AMD SVM Mode」が有効になっていることを確認します。AMD SVM ModeはIntelのVT-dと同様、CPUレベルの仮想化支援技術とのことです。  
次にWindows上でコントロールパネル → プログラム → Windowsの機能の有効化または無効化  
の項目でHyper-VとWindowsサンドボックスを有効化します。

![Windowsサンドボックスの有効化.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/87bdb522-1052-e563-3bf1-bb9b1956ccc9.png)

無効状態から有効にした場合は再起動が求められますので、再起動します。  

---

## 作業1 Hyper-V上で仮想マシンを作成

ホストWindows 11上でHyper-Vマネージャーを立ち上げ、新規仮想マシン作成ウィザードを起動します。

![仮想マシン作成ウィザード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/ef090e2a-2eb6-2cb0-05c0-f8c5a435e67f.png)

上図のようにHyper-Vマネージャ右側の「新規」→「仮想マシン」からウィザードを起動します。

![仮想マシンの新規作成ウィザード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/8d600272-369f-e6a5-68f8-49f6fd579cb7.png)

「次へ」をクリックしてウィザードを進めます。  
仮想マシンの名前を設定します。好きな名前を設定して構いません。

![仮想マシン名前と場所の指定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/1cbd2b69-9cc0-722d-bf06-d4b5c45198fc.png)

「次へ」をクリックしてウィザードを進めます。次は世代の指定です。第2世代を選択します。  
「次へ」をクリックしてウィザードを進めます。次はメモリの割り当てです。Windows 11の動作要件を満たしていることを前提に、好きな量のメモリを割り当てます。

「次へ」をクリックしてウィザードを進めます。次はネットワークの構成です。希望する接続に合わせどの選択肢を選んでもかまいません。後で設定から変更できます。

「次へ」をクリックして「仮想ハードディスクの接続」セクションに進みます。  
ここでは新しく仮想ハードディスクを作成します。既存のものがあるようならそれでも構いません。  
サイズは自分が仮想マシンで使いたいディスクサイズを指定します。

![仮想ハードディスクの接続.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/c3dbabe1-bb8c-5574-8581-ddb64ac73a2a.png)

「次へ」をクリックして「インストールオプション」のセクションへ進みます。「ブートイメージファイルからオペレーティングシステムをインストールする」を選択し、準備1でダウンロードしたWindows 11のインストールISOファイルを指定します。

これで新規作成ウィザードは完了です。「完了」をクリックして仮想マシンを作成します。

---

## 作業2 立ち上げ前の仮想マシン設定

Hyper-Vマネージャーから、今作成した仮想マシンを選択し、画面右の「設定」をクリックします。 

![仮想マシンの設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/a860c6c7-b75e-9812-de86-39b357f79663.png)

Windows 11をインストールする場合、「セキュリティ」の項で「トラステッドプラットフォームモジュールを有効にする」のチェックを入れます。  
その他、プロセッサ数などをお好みに変更します。

---

## 作業3 Hyper-V仮想ディスクをマウントする

後に仮想ディスクをホスト側からマウントできるようにする必要があるので、作業1で作成した仮想マシン用の仮想ハードディスク(`.vhdx`ファイル)をエクスプローラで表示し、右クリックから「マウント」を選択します。  
エラー表示がでると思いますが、仮想ハードディスクとしては認識されているので、コントロールパネル→「システムとセキュリティ」→「Windowsツール」下にある「ハードディスクパーティションの作成とフォーマット」を選択して、「ディスクの管理」を立ち上げます。  

「ディスクの管理」で先程マウントした仮想ハードディスクが認識されていますが、ファイルシステムが不明な状態になっています。この部分を右クリックして、新しくGPTファイルシステムを作成します。フォーマットはしてもしなくても構いません。  
ここでホスト側からファイルシステムを作らなくても仮想マシンにWindowsをインストールすることも可能ですが、私の環境ではインストール後にホスト側から仮想マシン側のファイルシステムが見えない状態となってしまい、後の作業に支障をきたしました。

ファイルシステムを作成したら「ディスク n」の部分を右クリック、「VHDの切断」を選択してマウントを解除します。

---

## 作業4 チェックポイントをオフにする

仮想マシンが停止している状態で、Hyper-Vマネージャーから仮想マシンの設定を選択し、「チェックポイント」の項目の「チェックポイントを有効にする」のチェックを外します。  
これから導入するGPU-Pはチェックポイント機能が有効になっていると使用できないからです。

---

## 作業5 仮想マシンを起動する

それでは作成が終了し、設定を行った仮想マシンの「接続」を選択します。仮想マシンへの接続セッションが立ち上がりますので、「起動」をクリックします。  
画面に「Press Any key to boot CD or DVD」の表示がでている数秒間のうちに素早くなんらかのキー入力を行うとWindows 11のインストーラが立ち上がります。  

ここからしばらくは通常どおりインストーラに従ってWindows 11をインストールします。上手くいかない場合もあり得ますので、プロダクトキーはまだいれなくてよいでしょう。

インストールが終了し、通常のデスクトップに入れるようになったら仮想マシンをシャットダウンします。

---

## 作業6 GPUパーティショニングを有効にする

ここからの作業はホストOSのPowerShellで行います。Windows PowerShellを管理者モードで起動し、次のコマンドを実行していきます。

まずは、マルチGPUマシンの場合どのGPUを仮想マシンに渡すかを決めるためにPowerShellで

```
> Get-VMHostPartitionableGpu
```

を実行します。

![Get-VMHostPartitionableGpu結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/71d1cb59-17af-55e9-7ea6-165ef21f09f9.png)

上図のようにGPUのリストが出力されますが、

```
Name                    : \\?\PCI#VEN_10DE&DEV_2204&SUBSYS_145410DE&REV_A1#4&251e7d7&0&0019#{064092b3-625e-43bf-9eb5-dc845897dd59}\GPUPARAV
```

のような「Name」が含まれた行の出力をコピーしておきます。私のようにマルチGPU環境では(恐らく)デバイスマネージャに表示されている順に複数GPUの情報が表示されます。次に以下のコマンド群を実行してゆきますが、最初の`Add-VMGpuPartitionAdapater`コマンドの`-InstancePath`の引数には上記のNameの内容をダブルクォーテーションで囲んでペーストします。  
ホストOSがWindows 10の場合はこの`InstancePath`による設定はできない(らしい)ので、`-InstancePath`のオプションは付けず`Set-VMGpuPartitionAdapter`の設定でプライマリGPUの能力を分割することになります。  
私の環境ではセカンドGPUはすべて仮想マシンに渡してしまって差し支えないので、`Set-VMGpuPartitionAdapter`コマンドの引数では`-Passthru`を指定しています。GPUの能力のうち一部を分割して仮想マシンに渡す場合はここで細かいパラメータを指定します(上記の@Hyper-W氏の記事にパラメータの記載例が掲載されています)。

```powershell
> Add-VMGpuPartitionAdapter -VMName "仮想マシン名" -InstancePath "Nameの内容"

> Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -Passthru

> Set-VM -GuestControlledCacheTypes $true -VMName "仮想マシン名"

> Set-VM -LowMemoryMappedIoSpace 1GB -VMName "仮想マシン名"

> Set-VM –HighMemoryMappedIoSpace 32GB –VMName "仮想マシン名"
```

---

## 作業7 Windowsサンドボックスの機能を用いてホストのGPUドライバとライブラリをHyper-V仮想マシンにコピーする

作業6の設定で仮想マシン上のデバイスマネージャからGPUが認識されるようになっているはずですが、まだドライバが仮想マシンにインストールされていないので正しく動作する状態になっていません。  
仮想マシン上でドライバをメーカーサイトからダウンロードしてインストールしても上手くいかないようですので、@Hyper-W氏の記事に従ってWindowsサンドボックスの機能を利用してホストGPUドライバを仮想マシンから使用できるようにします。

### 作業7-1 仮想ハードディスクをマウントする

「ディスクの管理」で仮想マシン用のVHDXファイルが見えている状態にします。Windowsがインストールされているパーティンションを右クリックし、「ドライブ文字とパスの変更」からドライブ文字の割り当てを行います。私はVドライブとしました。

![ドライブ文字割り当て.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/55716cf2-646f-74a1-d9db-3d5e266233e6.png)

エクスプローラで`V:\Windows\System32`フォルダに移動し、新規→フォルダで新しいフォルダを作成します。さらに作成したフォルダを「HostDriverStore」に名称変更します。`V:\Windows\System32\HostDriverStore`という空フォルダができることになります。

### 作業7-2 Windowsサンドボックス設定ファイルの作成

デスクトップなど、どこでも構わないので「新規テキストドキュメント.txt」を作成し、メモ帳で開きます。  
以下のテキストをペーストし、保存してメモ帳を終了します。ただしドライブ名だけは作業7-1で割り当てた仮想ディスクのドライブ名に変更します。

```
<Configuration>
<VGpu>Enable</VGpu>
<MappedFolders>
   <MappedFolder>
       <HostFolder>V:\Windows\System32\HostDriverStore</HostFolder>
       <ReadOnly>false</ReadOnly>
   </MappedFolder>
</MappedFolders>
</Configuration>
```

メモ帳を終了したら、「新規テキストドキュメント.txt」のファイル名を「drivercopy.wsb」に変更します。拡張子変更に関する警告ダイアログがでますが、そのまま続行して拡張子を変更します。  
今度はPowerShellでこのwsbファイルがあるディレクトリに移動します。

```powershell
> C:
> cd \Users\******\Desktop
```

### 作業7-3 Windowsサンドボックスを利用したドライバの仮想マシンへのコピー

作業7-2で作成した`drivercopy.wsb`をPowerShellから実行します。

```
> .\drivercopy.wsb
```

サンドボックスが立ち上がったら、デスクトップに共有した「HostDriverStore」フォルダがあるので、サンドボックス上の`C:\Windows\System32\HostDriverStore`フォルダの内容をデスクトップ上の「HostDriverStore」にコピーします(左ドラッグだと移動になってしまうので、右ドラッグやキーボード操作などでコピーして下さい)。  

![ドライバコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/b7e11a92-e7a3-cf04-a519-be0f1b9311d2.png)

ホスト側のエクスプローラで`V:\Windows\System32\HostDriverStore`が空ではなくコピーされた内容が存在することが確認できるはずですので、確認できたらサンドボックスは終了します。  
ホストに作成した`drivercopy.wsb`は削除しても構いませんが、ホスト側でグラフィックドライバを更新したときに同様の作業が必要になりますので、保存しておいてもよいでしょう。


### 作業7-4 仮想ディスクのアンマウントとHyper-V仮想マシンの起動

ホストOSの「ディスクの管理」で仮想マシン用のディスクを右クリック→「VHDの切断」でアンマウントします。  
いよいよこれで仮想マシン上でGPUが使えるようになっているはずです。Hyper-Vマネージャーから仮想マシンを起動しましょう。
デバイスマネージャでGPUが正常動作しているのが確認できるはずです。

![GPU動作確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1845251/4fe1b517-51b2-6a84-d368-066964173bcb.png)

Windows 11のタスクバーに背景が透過されていることからGPUアクセラレーションが効いていることがわかります。  
~~Hyper-Vの仮想マシンはリモートセッション扱いになるので~~アプリケーション/ゲームの一部では動作しないこともあるかもしれませんが、これでGPUアクセラレーションが有効な仮想Windowsを動作させるという目標を達成したことになります。  
(リモートセッション扱いになるのは拡張セッションのみだと教えて頂きましたので訂正します。)

---

## 問題点

今回の方法では仮想マシン上で`nvidia-smi`を実行しても

```powershell
> cd (ドライバーが保存されているフォルダ)
> nvidia-smi.exe
Failed to initialize NVML: Unknown Error
```

のエラーが起こりました。一部のアプリケーションではGPUが認識されない問題も残っています。現時点では筆者は解決策を得られていません。今後NVIDIAやMicrosoftのアップデートで改善されることに期待したいと思います。  
なお、`nvapi64.dll`を`C:\Windows\System32`にホストOSからコピーしましたが、残念ながら私の環境では同じエラーが出て`nvidia-smi`は正しく動作しませんでした。

---

## 補足1: ホスト側でグラフィックドライバを更新したときは

仮想マシン側のドライバは自動では更新されないので、仮想マシンをシャットダウンした状態で仮想ハードディスクをマウントし、作業7-3による仮想マシン側へのドライバコピーを再度行って下さい。

---

## 補足2: 元に戻したいときは

仮想マシンの使用をやめ、GPUを完全にホストに戻したいときは、ホスト側のPowerShell(管理者)で

```powershell
> Remove-VMGpuPartitionAdapter -VMName "仮想マシン名"
```

とすればOKです。