Wouter Verhelst: Upgrading a Windows 10 VM to Windows 11

- Secure Boot is required (Windows 10 would still boot on a machine without Secure Boot, although buying hardware without at least support for that hasn't been possible for several years now)
- A v2.0 TPM module (Windows 10 didn't need any TPM)
- A modern enough processor.
A modern enough processor
If your processor isn't modern enough to run Windows 11, then you can
probably forget about it (unless you want to use qemu JIT compilation --
I dunno, probably not going to work, and also not worth it if it were).
If it is, all you need is the "host-passthrough" setting in libvirt,
which I've been using for a long time now. Since my laptop is less than
two months
old, that's not a
problem for me.
A TPM 2.0 module
My Windows 10 VM did not have a TPM configured, because it wasn't
needed. Luckily, a quick web search told me that enabling that is not
hard. All you need to do is:
- Install the
swtpm
and swtpm-tools
packages
- Adding the TPM module, by adding the following XML snippet to your VM
configuration:
<devices>
<tpm model='tpm-tis'>
<backend type='emulator' version='2.0'/>
</tpm>
</devices>
Alternatively, if you prefer the graphical interface, click on the
"Add hardware" button in the VM properties, choose the TPM, set it
to Emulated, model TIS, and set its version to 2.0.
You're done!
Well, with this part, anyway. Read on.
Secure boot
Here is where it gets interesting.
My Windows 10 VM was old enough that it was configured for the older
i440fx
chipset. This one is limited to PCI and IDE, unlike the more
modern q35
chipset (which supports PCIe and SATA, and does not support
IDE nor SATA in IDE mode).
There is a UEFI/Secure Boot-capable BIOS for qemu, but it apparently
requires the q35
chipset,
Fun fact (which I found out the hard way): Windows stores where its boot
partition is somewhere. If you change the hard drive controller from an
IDE one to a SATA one, you will get a BSOD at startup. In order to fix
that, you need a recovery
drive.
To create the virtual USB disk, go to the VM properties, click "Add
hardware", choose "Storage", choose the USB bus, and then under
"Advanced options", select the "Removable" option, so it shows up as a
USB stick in the VM. Note: this takes a while to do (took about an hour
on my system), and your virtual USB drive needs to be 16G or larger (I
used the libvirt default of 20G).
There is no possibility, using the buttons in the virt-manager
GUI, to
convert the machine from i440fx
to q35
. However, that doesn't mean
it's not possible to do so. I found that the easiest way is to use the
direct XML editing capabilities in the virt-manager
interface; if you
edit the XML in an editor it will produce error messages if something
doesn't look right and tell you to go and fix it, whereas the
virt-manager
GUI will actually fix things itself in some cases (and
will produce helpful error messages if not).
What I did was:
- Take backups of everything. No, really. If you fuck up, you'll have
to start from scratch. I'm not responsible if you do.
- Go to the Edit->Preferences option in the VM manager, then on the
"General" tab, choose "Enable XML editing"
- Open the Windows VM properties, and in the "Overview" section, go to
the "XML" tab.
- Change the value of the
machine
attribute of the domain.os.type
element, so that it says pc-q35-7.0
.
- Search for the
domain.devices.controller
element that has pci
in
its type
attribute and pci-root
in its model
one, and set the
model
attribute to pcie-root
instead.
- Find all
domain.devices.disk.target
elements, setting their
dev=hdX
to dev=sdX
, and bus="ide"
to bus="sata"
- Find the USB controller (
domain.devices.controller
with
type="usb"
, and set its model
to qemu-xhci
. You may also want to
add ports="15"
if you didn't have that yet.
- Perhaps also add a few PCIe root ports:
<controller type="pci" index="1" model="pcie-root-port"/>
<controller type="pci" index="2" model="pcie-root-port"/>
<controller type="pci" index="3" model="pcie-root-port"/>
I figured out most of this by starting the process for creating a new
VM, on the last page of the wizard that pops up selecting the "Modify
configuration before installation" option, going to the "XML" tab on the
"Overview" section of the new window that shows up, and then comparing
that against what my current VM had.
Also, it took me a while to get this right, so I might have forgotten
something. If virt-manager
gives you an error when you hit the Apply
button, compare notes against the VM that you're in the process of
creating, and copy/paste things from there to the old VM to make the
errors go away. As long as you don't remove configuration that is
critical for things to start, this shouldn't break matters permanently
(but hey, use your backups if you do break -- you have backups, right?)
OK, cool, so now we have a Windows VM that is... unable to boot.
Remember what I said about Windows storing where the controller is?
Yeah, there you go. Boot from the virtual USB disk that you created
above, and select the "Fix the boot" option in the menu. That will fix
it.
Ha ha, only kidding. Of course it doesn't.
I honestly can't tell you everything that I fiddled with, but I
think the bit that eventually fixed it was where I chose "safe mode",
which caused the system to do a hickup, a regular reboot, and then
suddenly everything was working again. Meh.
Don't throw the virtual USB disk away yet, you'll still need it.
Anyway, once you have it booting again, you will now have a machine that
theoretically supports Secure Boot, but you're still running off an
MBR partition. I found a
procedure
on how to convert things from MBR to GPT that was written almost 10
years ago, but surprisingly it still works, except for the bit where the
procedure suggests you use diskmgmt.msc (for one thing, that was
renamed; and for another, it can't touch the partition table of the
system disk either).
The last step in that procedure says to restart your computer!,
which is fine, except at this point you obviously need to switch over to
the TianoCore firmware, otherwise you're trying to read a UEFI boot
configuration on a system that only supports MBR booting, which
obviously won't work. In order to do that, you need to add a loader
element to the domain.os
element of your libvirt configuration:
<loader readonly="yes" type="pflash">/usr/share/OVMF/OVMF_CODE_4M.ms.fd</loader>
When you do this, you'll note that virt-manager
automatically adds an
nvram
element. That's fine, let it.
I figured this out by looking at the documentation for enabling Secure
Boot in a VM on the
Debian wiki, and using the same trick as for how to switch chipsets that
I explained above.
Okay, yay, so now secure boot is enabled, and we can install Windows 11!
All good? Well, almost.
I found that once I enabled secure boot, my display reverted to a
1024x768 screen. This turned out to be because I was using older
unsigned drivers, and since we're using Secure Boot, that's no longer
allowed, which means Windows reverts to the default VGA driver, and that
only supports the 1024x768 resolution. Yeah, I know. The solution is
to download the virtio-win ISO from one of the links in the virtio-win
github project,
connecting it to the VM, going to Device manager, selecting the display
controller, clicking on the "Update driver" button, telling the system
that you have the driver on your computer, browsing to the CD-ROM drive,
clicking the "include subdirectories" option, and then tell Windows to
do its thing. While there, it might be good to do the same thing for
unrecognized devices in the device manager, if any.
So, all I have to do next is to get used to the completely different
user interface of Windows 11. Sigh.
Oh, and to rename the "w10" VM to "w11", or some such. Maybe.
- Install the
swtpm
andswtpm-tools
packages - Adding the TPM module, by adding the following XML snippet to your VM
configuration:
Alternatively, if you prefer the graphical interface, click on the "Add hardware" button in the VM properties, choose the TPM, set it to Emulated, model TIS, and set its version to 2.0.<devices> <tpm model='tpm-tis'> <backend type='emulator' version='2.0'/> </tpm> </devices>
Secure boot
Here is where it gets interesting.
My Windows 10 VM was old enough that it was configured for the older
i440fx
chipset. This one is limited to PCI and IDE, unlike the more
modern q35
chipset (which supports PCIe and SATA, and does not support
IDE nor SATA in IDE mode).
There is a UEFI/Secure Boot-capable BIOS for qemu, but it apparently
requires the q35
chipset,
Fun fact (which I found out the hard way): Windows stores where its boot
partition is somewhere. If you change the hard drive controller from an
IDE one to a SATA one, you will get a BSOD at startup. In order to fix
that, you need a recovery
drive.
To create the virtual USB disk, go to the VM properties, click "Add
hardware", choose "Storage", choose the USB bus, and then under
"Advanced options", select the "Removable" option, so it shows up as a
USB stick in the VM. Note: this takes a while to do (took about an hour
on my system), and your virtual USB drive needs to be 16G or larger (I
used the libvirt default of 20G).
There is no possibility, using the buttons in the virt-manager
GUI, to
convert the machine from i440fx
to q35
. However, that doesn't mean
it's not possible to do so. I found that the easiest way is to use the
direct XML editing capabilities in the virt-manager
interface; if you
edit the XML in an editor it will produce error messages if something
doesn't look right and tell you to go and fix it, whereas the
virt-manager
GUI will actually fix things itself in some cases (and
will produce helpful error messages if not).
What I did was:
- Take backups of everything. No, really. If you fuck up, you'll have
to start from scratch. I'm not responsible if you do.
- Go to the Edit->Preferences option in the VM manager, then on the
"General" tab, choose "Enable XML editing"
- Open the Windows VM properties, and in the "Overview" section, go to
the "XML" tab.
- Change the value of the
machine
attribute of the domain.os.type
element, so that it says pc-q35-7.0
.
- Search for the
domain.devices.controller
element that has pci
in
its type
attribute and pci-root
in its model
one, and set the
model
attribute to pcie-root
instead.
- Find all
domain.devices.disk.target
elements, setting their
dev=hdX
to dev=sdX
, and bus="ide"
to bus="sata"
- Find the USB controller (
domain.devices.controller
with
type="usb"
, and set its model
to qemu-xhci
. You may also want to
add ports="15"
if you didn't have that yet.
- Perhaps also add a few PCIe root ports:
<controller type="pci" index="1" model="pcie-root-port"/>
<controller type="pci" index="2" model="pcie-root-port"/>
<controller type="pci" index="3" model="pcie-root-port"/>
I figured out most of this by starting the process for creating a new
VM, on the last page of the wizard that pops up selecting the "Modify
configuration before installation" option, going to the "XML" tab on the
"Overview" section of the new window that shows up, and then comparing
that against what my current VM had.
Also, it took me a while to get this right, so I might have forgotten
something. If virt-manager
gives you an error when you hit the Apply
button, compare notes against the VM that you're in the process of
creating, and copy/paste things from there to the old VM to make the
errors go away. As long as you don't remove configuration that is
critical for things to start, this shouldn't break matters permanently
(but hey, use your backups if you do break -- you have backups, right?)
OK, cool, so now we have a Windows VM that is... unable to boot.
Remember what I said about Windows storing where the controller is?
Yeah, there you go. Boot from the virtual USB disk that you created
above, and select the "Fix the boot" option in the menu. That will fix
it.
Ha ha, only kidding. Of course it doesn't.
I honestly can't tell you everything that I fiddled with, but I
think the bit that eventually fixed it was where I chose "safe mode",
which caused the system to do a hickup, a regular reboot, and then
suddenly everything was working again. Meh.
Don't throw the virtual USB disk away yet, you'll still need it.
Anyway, once you have it booting again, you will now have a machine that
theoretically supports Secure Boot, but you're still running off an
MBR partition. I found a
procedure
on how to convert things from MBR to GPT that was written almost 10
years ago, but surprisingly it still works, except for the bit where the
procedure suggests you use diskmgmt.msc (for one thing, that was
renamed; and for another, it can't touch the partition table of the
system disk either).
The last step in that procedure says to restart your computer!,
which is fine, except at this point you obviously need to switch over to
the TianoCore firmware, otherwise you're trying to read a UEFI boot
configuration on a system that only supports MBR booting, which
obviously won't work. In order to do that, you need to add a loader
element to the domain.os
element of your libvirt configuration:
<loader readonly="yes" type="pflash">/usr/share/OVMF/OVMF_CODE_4M.ms.fd</loader>
When you do this, you'll note that virt-manager
automatically adds an
nvram
element. That's fine, let it.
I figured this out by looking at the documentation for enabling Secure
Boot in a VM on the
Debian wiki, and using the same trick as for how to switch chipsets that
I explained above.
Okay, yay, so now secure boot is enabled, and we can install Windows 11!
All good? Well, almost.
I found that once I enabled secure boot, my display reverted to a
1024x768 screen. This turned out to be because I was using older
unsigned drivers, and since we're using Secure Boot, that's no longer
allowed, which means Windows reverts to the default VGA driver, and that
only supports the 1024x768 resolution. Yeah, I know. The solution is
to download the virtio-win ISO from one of the links in the virtio-win
github project,
connecting it to the VM, going to Device manager, selecting the display
controller, clicking on the "Update driver" button, telling the system
that you have the driver on your computer, browsing to the CD-ROM drive,
clicking the "include subdirectories" option, and then tell Windows to
do its thing. While there, it might be good to do the same thing for
unrecognized devices in the device manager, if any.
So, all I have to do next is to get used to the completely different
user interface of Windows 11. Sigh.
Oh, and to rename the "w10" VM to "w11", or some such. Maybe.
machine
attribute of the domain.os.type
element, so that it says pc-q35-7.0
.domain.devices.controller
element that has pci
in
its type
attribute and pci-root
in its model
one, and set the
model
attribute to pcie-root
instead.domain.devices.disk.target
elements, setting their
dev=hdX
to dev=sdX
, and bus="ide"
to bus="sata"
domain.devices.controller
with
type="usb"
, and set its model
to qemu-xhci
. You may also want to
add ports="15"
if you didn't have that yet.<controller type="pci" index="1" model="pcie-root-port"/>
<controller type="pci" index="2" model="pcie-root-port"/>
<controller type="pci" index="3" model="pcie-root-port"/>
<loader readonly="yes" type="pflash">/usr/share/OVMF/OVMF_CODE_4M.ms.fd</loader>