2022/06/26

QEMU+KVM+SpiceでUEFI+TPM2.0のWindows11の導入

QEMU上でWindows10の仮想PCを動かしていたが、そろそろWindows11に対応冴えておく必要があると思い、アップグレードしようとした。

結論的には、Win10はBIOSで動いていたのに対し、Win11はUEFIセキュアブートにしなければならなく、HDDイメージにEFIパーティションなかったので新規インストールをした。

Win10のときと同様、SpiceのRemote viewerに音が流れてこない問題は残っているが、一応完了したのでメモ。


Windows11のISOイメージを用意する。

兎にも角にも、Windows11のISOイメージは必要。

https://www.microsoft.com/ja-jp/software-download/windows11


UEFIファームウェアを用意する。

UEFIのファームウェアイメージが必要になるので、EDK IIのOVMFを導入する。が、すでにインストールされていた(sys-firmware/edk2-ovmf)。

OVMFのイメージの中に、UEFIの設定情報まで書き込まれてしまうため、仮想VM1台毎に1つのイメージを作ることになる。ただ、1台毎にOVMFのイメージ全体を用意するのはディスクがもったいないので、設定情報を保存する部分だけを切り出したイメージと併用する。

# cp -a /usr/share/edk2-ovmf/OVMF_VARS.fd /data/VM_images/OVMF_VARS_win11.fd

セキュアブートができるイメージ OVMF_CODE.secboot.fd を利用するのだが、SMMサポートありになっているため、-machineオプションにsmm=onを足す必要がある。また、S3サポートが有効になっていると起動してこないため、-globalオプションの中でICH9-LPC.disable_s3=1を入れてS3を無効化する必要がある。

このあたりが参考情報。


仮想TPMを用意する。

仮想TPMの導入のため、app-crypt/swtpmを導入する。

# emerge -uDNtpv swtpm

QEMUで仮想VMを呼び出す前に、swtpmで作った仮想TPMを起動させる必要がある。サービス化しようとも考えたが、仮想VM起動時に仮想VMが仮想TPMを掴んだ後、その仮想VMを終了させると仮想TPMのプロセスも無くなってしまうような挙動をしているので、仮想VM起動スクリプトに埋め込んだ。

virt-managerを使っていれば、swtpmを起動時に自動的に呼び出してくれる設定もあるらしいが、うちではqemuを直接呼び出しているので、対象外。

今回は、ソケットを使って仮想TPMをゲストOSに引き渡すことにした。

# mkdir /data/VM_images/tpm0
# swtpm socket --tpm2 --tpmstate dir=/data/VM_images/tpm0 --ctrl type=unixio,path=/data/VM_images/tpm0/swtpm-sock --log level=20

最後の--log level=20は最終的には削除して、画面上にログが出てくるのを抑止した。


virtioのためのドライバを用意する。

virtioのドライバは、Windows11のインストール時の標準ドライバとして用意されていないため、virtio-win.isoを用意しておく

https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/から最新バージョンをダウンロード


OSを入れるための仮想ディスクを用意する。

シンプロビジョニングされるのと、Win11の要件を満たすため64Gのサイズに。(Win11の要件:https://www.microsoft.com/ja-jp/windows/windows-11-specifications)

# qemu-img create -f qcow2 Win11VM_master.img 64G
Formatting 'Win11VM_master.img', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=68719476736 lazy_refcounts=off refcount_bits=16


QEMU起動のためのスクリプトを用意

今までWin10用に使っていたものを再利用して、Win11で必要なTPM 2.0やUEFIセキュアブートの設定を追加。

ちなみに、ここではサウンドカードにAC97を指定しているが、Win10で使えたドライバがWin11では使えなくなっているので、最終的にはintel-hdaに変更した。

下記のスクリプトで、前半の太字しているところがUEFIセキュアブートの設定、後半の太字のところが仮想TPM 2.0の設定。

#!/bin/sh

SPICE_PORT=5924
echo "spice://192.168.1.2:${SPICE_PORT}"

swtpm socket --tpm2 --tpmstate dir=/data/VM_images/tpm0 --ctrl type=unixio,path=/data/VM_images/tpm0/swtpm-sock &

exec qemu-system-x86_64 \
  \
  -global driver=cfi.pflash01,property=secure,value=on \
  -drive if=pflash,format=raw,unit=0,file=/usr/share/edk2-ovmf/OVMF_CODE.secboot.fd,readonly=on \
  -drive if=pflash,format=raw,unit=1,file=/data/VM_images/OVMF_VARS_win11.fd \
  \
  -machine type=q35,smm=on,accel=kvm \
  -global ICH9-LPC.disable_s3=1 \
  \
  -object rng-random,id=rng0,filename=/dev/urandom -device virtio-rng-pci,rng=rng0 \
  -enable-kvm \
  -cpu host \
  -cdrom /data2/Win11_Japanese_x64v1.iso \
  -drive file=/data2/virtio-win.iso,media=cdrom \
  -drive file=/data/VM_images/Win11VM_master.img,if=virtio,index=0,media=disk,cache=none \
  -net nic,macaddr=00:00:00:00:00:02,model=virtio -net tap,ifname=tap0,script=no,downscript=no,vhost=on \
  -m 4G \
  -name "Windows" \
  -rtc base=localtime,clock=host \
  -smp 4,sockets=1,cores=4 \
  -spice port=${SPICE_PORT},addr=0.0.0.0,disable-ticketing=on,streaming-video=filter,playback-compression=on \
  -audiodev spice,id=snd0 \
  -device AC97,audiodev=snd0 \
  -device virtio-serial-pci \
  -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 \
  -chardev spicevmc,id=spicechannel0,name=vdagent \
  \
  -chardev socket,id=tpmc,path=/data/VM_images/tpm0/swtpm-sock \
  -tpmdev emulator,id=tpm0,chardev=tpmc \
  -device tpm-tis,tpmdev=tpm0 \
  \
  -vnc 0.0.0.0:0 -k ja \
  -monitor stdio \
  -boot once=d \
  "$@"


仮想VMを起動

上記シェルスクリプトを起動すると、Remote Viewerで「Press any key to boot from CD or DVD......」と表示されるので、タイムアウトする前になにかキーを押してDVDからの起動を行う。

あとは通常のWindowsのインストールと同じなので省略するが、途中のインストール先ディスクの指定の際、仮想ディスクが見えていない点に注意が必要。


これは、前述のvirtioのドライバがないため、ディスクを見つけられないことによる。「ドライバーの読み込み(L)」のところからvirtio-win.isoに入っているドライバを取り込む。

isoイメージの直下にamd64とi386とがあるので、利用しているCPUに合わせて選択し、w11もしくはw10のフォルダを選んでやると後はWindowsがうまく処理してくれる。

Windows11のインストールが完了したら、virtio-win.isoの「virtio-win-guest-tools.exe」で必要なドライバを一式導入する。

ちなみに、spice-guest-tools.exeを入れると、Spice VD agentサービスが2個登録されてしまうので、spice-guest-tools.exeは入れなくても良さそう(しかもspice-guest-toolsに入っているvdservice.exeの方が更新日時が古い)。


起動スクリプトの修正

インストールが終わると、Win11のイメージやvirtio-win.isoを読み込む必要がなくなるので、起動スクリプトから削除する。

Win10のときからだが、物理PCを入れ替えてCPUをCore i7からRyzenにしてから、-vga qxlをつけて起動すると、起動時に「qxl_send_events: spice-server bug: guest stopped, ignoring」と警告が出る。

Win10の際は、画面を見るとWindowsの修復モードに入っていたのでqxlは使わないようにしていた。ただ、Win11にしてから起動したら、初回は警告も出ずに修復モードにも入らなかったので、解決されたのかと思っていたら、しばらくしたたら警告も出るし修復モードに入るようになったので、再度qxlを使わないようにした。

その後、いろいろと調べていた際、spice-guest-toolsを削除して起動すると、警告は出るものの修復モードには入らないことを発見したので、qxlを有効にした。


#!/bin/sh

SPICE_PORT=5924
echo "spice://192.168.1.2:${SPICE_PORT}"

swtpm socket --tpm2 --tpmstate dir=/data/VM_images/tpm0 --ctrl type=unixio,path=/data/VM_images/tpm0/swtpm-sock &

exec qemu-system-x86_64 \
  \
  -global driver=cfi.pflash01,property=secure,value=on \
  -drive if=pflash,format=raw,unit=0,file=/usr/share/edk2-ovmf/OVMF_CODE.secboot.fd,readonly=on \
  -drive if=pflash,format=raw,unit=1,file=/data/VM_images/OVMF_VARS_win11.fd \
  \
  -machine type=q35,smm=on,accel=kvm \
  -global ICH9-LPC.disable_s3=1 \
  \
  -device ich9-ahci,id=ahci \
  -object rng-random,id=rng0,filename=/dev/urandom -device virtio-rng-pci,rng=rng0 \
  -enable-kvm \
  -cpu host \
  -drive file=/data/VM_images/Win11VM_master.img,if=virtio,index=0,media=disk,cache=none \
  -net nic,macaddr=00:00:00:00:00:02,model=virtio -net tap,ifname=tap0,script=no,downscript=no,vhost=on \
  -m 4G \
  -name "Windows" \
  -rtc base=localtime,clock=host \
  -smp 4,sockets=1,cores=4 \
  -spice port=${SPICE_PORT},addr=0.0.0.0,disable-ticketing=on,streaming-video=filter,playback-compression=on \
  \
  -audiodev spice,id=snd0 \
  -device intel-hda \
  -device hda-output,audiodev=snd0 \
  \
  -device virtio-serial-pci \
  -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 \
  -chardev spicevmc,id=spicechannel0,name=vdagent \
  \
  -chardev socket,id=tpmc,path=/data/VM_images/tpm0/swtpm-sock \
  -tpmdev emulator,id=tpm0,chardev=tpmc \
  -device tpm-tis,tpmdev=tpm0 \
  \
  -vnc 0.0.0.0:0 -k ja \
  -monitor stdio \
  -vga qxl \
  "$@"



ハマったこと

「Guest has not initialized the display (yet).」エラー

Win10のイメージをそのまま利用してUEFIのセキュアブート(OVMF_CODE.secboot.fdを使用)をしようとしたら「Guest has not initialized the display (yet).」とだけ出て、先に進まない。

これは、前述のSMMの有効化設定や、S3の無効化設定が入っていないためで、どちらがかけていても同じエラーになる。


ブート可能デバイスが見つからない

セキュアブートではないUEFIファームウェア(OVMF_CODE.fdを使用)しても、上記のセキュアブート対応のUEFIの問題を解決しても、以下のようなエラーが出て起動イメージを見つけることができない。

「BdsDxe: failed to load Boot0001 "UEFI QEMU DEV-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0): Not Found」

「BdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x5,0x0): Not Found」

「Start PXE over IPv4.」

これは、Win10のディスクイメージがBIOSを想定しており、EFIパーティションが作成されていなかったため。

ちなみに、「Boot0001」や「Boot0002」は、ブートの優先順位1位、2位を表していると思われる。


「Press any key to boot from CD or DVD......」と出ているが、UEFIがエラーを出している。


これは、SpiceのRemote Viewerを立ち上げるのが遅く、DVDからの起動待ち時間が過ぎてしまったため、UEFIが違うデバイスから起動させようとしているために発生している。

エラーメッセージは「BdsDxe: failed to start Boot0001 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFF,0x0): Time out」で、最後に「Time out」が出ているので間違いなさそう。

QEMUのコマンドを叩く直前にRemote Viewerを立ち上げておくと良い。

もしくは、UEFIの設定画面が立ち上がったあと、「Boot Maintenance Manager」→「」→「Boot From File」→「EFISECTOR,~~~~/CDROM(~~~」→「EFI」→「BOOT」→「BOOTX64.EFI」の順で選んでやるとDVDから起動できる。













UEFIの設定画面に入れない

物理PCでは、起動時に(メーカーによって異なるが)F2とかを押すと、UEFIの設定画面に入れるが、OVMFではできなかった。

代わりに、IPv4、IPv6でのPXEブートが失敗して、Shellが起動した後、ExitしてやるとUEFIの設定画面に入れる。解像度の設定もできるが、最初は設定されていないので、すごく小さな画面に表示される。解像度の設定は、次回起動時から有効になる。


追記:2022年8月13日
UEFIの設定画面に入るのにはF2キーを使える。しかし、F2キーを押せるタイミングが非常に短いので、再起動してOSが落ちきる直前からF2キーを押し続けておくと、UEFIの設定画面に入れた。


 

0 件のコメント:

コメントを投稿