I run Debian on my laptop (obviously); but occasionally, for $DAYJOB, I have some work to do on Windows. In order to do so, I have had a Windows 10 VM in my libvirt configuration that I can use.
A while ago, Microsoft issued Windows 11. I recently found out that all the components for running Windows 11 inside a libvirt VM are available, and so I set out to upgrade my VM from Windows 10 to Windows 11. This wasn't as easy as I thought, so here's a bit of a writeup of all the things I ran against, and how I fixed them.
Windows 11 has a number of hardware requirements that aren't necessary for Windows 10. There are a number of them, but the most important three are:
- 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.
So let's see about all three.
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
andswtpm-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 thedomain.os.type
element, so that it sayspc-q35-7.0
. - Search for the
domain.devices.controller
element that haspci
in itstype
attribute andpci-root
in itsmodel
one, and set themodel
attribute topcie-root
instead. - Find all
domain.devices.disk.target
elements, setting theirdev=hdX
todev=sdX
, andbus="ide"
tobus="sata"
- Find the USB controller (
domain.devices.controller
withtype="usb"
, and set itsmodel
toqemu-xhci
. You may also want to addports="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.
Sometimes, it's useful to get a notification that a command has finished doing something you were waiting for:
make my-large-program && notify-send "compile finished" "success" || notify-send "compile finished" "failure"
This will send a notification message with the title "compile finished", and a body of "success" or "failure" depending on whether the command completed successfully, and allows you to minimize (or otherwise hide) the terminal window while you do something else, which can be a very useful thing to do.
It works great when you're running something on your own machine, but what if you're running it remotely?
There might be something easy to do, but I whipped up a bit of Perl instead:
#!/usr/bin/perl -w
use strict;
use warnings;
use Glib::Object::Introspection;
Glib::Object::Introspection->setup(
basename => "Notify",
version => "0.7",
package => "Gtk3::Notify",
);
use Mojolicious::Lite -signatures;
Gtk3::Notify->init();
get '/notify' => sub ($c) {
my $msg = $c->param("message");
if(!defined($msg)) {
$msg = "message";
}
my $title = $c->param("title");
if(!defined($title)) {
$title = "title";
}
app->log->debug("Sending notification '$msg' with title '$title'");
my $n = Gtk3::Notify::Notification->new($title, $msg, "");
$n->show;
$c->render(text => "OK");
};
app->start;
This requires the packages libglib-object-introspection-perl
,
gir1.2-notify-0.7
, and libmojolicious-perl
to be installed, and can
then be started like so:
./remote-notify daemon -l http://0.0.0.0:3000/
(assuming you did what I did and saved the above as "remote-notify")
Once you've done that, you can just curl a notification message to yourself:
curl 'http://localhost:3000/notify?title=test&message=test+body'
Doing this via localhost is rather silly (much better to use notify-send for that), but it becomes much more interesting if you're going to run this to your laptop from a remote system.
An obvious TODO would be to add in some form of security, but that's left as an exercise to the reader...
A notorious ex-DD decided to post garbage on his site in which he links my name to the suicide of Frans Pop, and mentions that my GPG key is currently disabled in the Debian keyring, along with some manufactured screenshots of the Debian NM site that allegedly show I'm no longer a DD. I'm not going to link to the post -- he deserves to be ridiculed, not given attention.
Just to set the record straight, however:
Frans Pop was my friend. I never treated him with anything but respect. I do not know why he chose to take his own life, but I grieved for him for a long time. It saddens me that Mr. Notorious believes it a good idea to drag Frans' name through the mud like this, but then, one can hardly expect anything else from him by this point.
Although his post is mostly garbage, there is one bit of information that is correct, and that is that my GPG key is currently no longer in the Debian keyring. Nothing sinister is going on here, however; the simple fact of the matter is that I misplaced my OpenPGP key card, which means there is a (very very slight) chance that a malicious actor (like, perhaps, Mr. Notorious) would get access to my GPG key and abuse that to upload packages to Debian. Obviously we can't have that -- certainly not from him -- so for that reason, I asked the Debian keyring maintainers to please disable my key in the Debian keyring.
I've ordered new cards; as soon as they arrive I'll generate a new key and perform the necessary steps to get my new key into the Debian keyring again. Given that shipping key cards to South Africa takes a while, this has taken longer than I would have initially hoped, but I'm hoping at this point that by about halfway September this hurdle will have been taken, meaning, I will be able to exercise my rights as a Debian Developer again.
As for Mr. Notorious, one can only hope he will get the psychiatric help he very obviously needs, sooner rather than later, because right now he appears to be more like a goat yelling in the desert.
Ah well.