The information surrounding this is very scattered, and sometimes even completely false, so I thought I'd collect my experience here. I assume you're at least somewhat familiar with GPU passthrough and how to set one up, so I'll be skipping the basics and cutting right to the main challenges of Windows 7.
Windows 7 is tough for GPU passthrough because the most widely supported path is having the guest boot through UEFI. Specifically, OVMF, the implementation most commonly used with QEMU. UEFI was the firmware standard that finally dethroned the IBM PC BIOS' decades-long reign, however that dethroning happened some time between Windows 7 and Windows 8, and as such, 7 only supports it partially.
I won't pretend to fully understand this myself, but essentially even though Windows 7 can boot in UEFI mode, it relies on some legacy BIOS interrupt(s) for graphics initialization that are not provided in pure UEFI. UEFI has an optional feature called CSM (Compatibility Support Module) that provides those interrupts (along with a general BIOS implementation), but CSM is slowly being phased out since modern OSes no longer need it.
There is a version of OVMF with CSM, and Windows 7 can be installed with it, however this variant appears to have poor support for PCI-e passthrough. OVMF with pure EFI has much better support, but doesn't have the interrupts Windows 7 requires to boot.
Well why use UEFI at all when Windows 7 doesn't even fully support it? Well, there are very mixed opinions on whether passthrough is possible with SeaBIOS (QEMU's default BIOS implementation) or not. There's a lot of posts saying it's significantly more difficult, that there are strict limitations like your host must also be booted in BIOS mode, and various other posts discouraging its use.
Seemingly what we really need is a way for Windows 7 to play nice with pure UEFI, but this is easier said than done...
What I Tried
There are a surprising amount of people that are trying to boot Windows 7 on UEFI, usually to try getting it to run on more modern hardware. Windows 7 really had staying power, didn't it?
What will most likely happen if you try to boot Windows 7 on pure UEFI is it'll crash at "Starting Windows". Normally, Windows 7 will display "Starting Windows", and then 4 colored orbs will animate to form the Windows flag. However, I guess somewhere before that happens (or maybe as part of it), that BIOS interrupt is called, and because it isn't present, Windows will either hang or immediately reboot.
I thought it might be helpful to work with an existing installation rather than the installer, so using the CSM variant of OVMF, I installed Windows 7 normally with a regular virtualized GPU (QXL). This went smoothly, and I soon had a functional install. It was very tempting to try passing through the GPU right now, but it didn't work. With both GPUs connected (real and QXL), UEFI would render to the card, but Windows would not. It would see the card in Device Manager, and it lets you install drivers, but then tells you the card couldn't start due to "Code 12" or "This device cannot find enough free resources to use." Depending on who you talk to, this might be because Windows 7 is too picky to have both a virtual GPU and a real GPU at the same time, or because you need to be more careful about PCI port assignment or something? Unfortunately, disabling the virtual GPU didn't help either - it simply refused to display any image at all, even the UEFI now for some reason.
UefiSeven is a pre-loader for Windows 7's UEFI that installs the missing interrupt so Windows can use it as necessary. In theory, this sounds like the perfect solution, but for some reason I could never get it to work, it would still freeze or crash at "Starting Windows". There seem to be a lot of GitHub issues saying the same thing, so perhaps it only works on some hardware configurations (or it's a completely separate compatibility issue that happens to have a very similar presentation?)
FlashBoot Pro - another pre-loader of sorts that claims to patch the Windows 7 loader to function correctly on pure UEFI. I never actually tried this because the version that does this is paid, but I bring it up because their developers wrote some interesting information about why the freeze on "Starting Windows" may occur. They say it's actually a BSOD that can't display properly because it's unable to switch to 640x480x16 mode due to the lack of CSM/BIOS. This actually checks out with my experience, because I noticed the VM would itself would "pause", indicating that the CPU had actually completely halted.
Patched OVMF for Windows 7 - I believe an alternative to UefiSeven that patches something directly in the firmware rather than at runtime in software. Unfortunately, this too didn't seem to help, either with or without UefiSeven installed, it would exhibit some kind of unusual behavior where instead of hanging or crashing at "Starting Windows", it would switch immediately to a progress bar saying "Windows is loading files..." (the same one you get when booting the Windows 7 installer or repair partition). It would load for a few seconds, and then crash.
Claims that it would work if I ensured it was PCI-e instead of PCI - I'm not super sure about this one, but I read some claims that the PCI-e devices are passed through by default behind a "PCI-e to PCI bridge", and that's what led to the aforementioned Code 12 issue. I have no idea if that's true, but their assertion was if you forced it to be PCI-e (which can only be done with QEMU command line rather than libvirt), it would work. Unfortunately this didn't help me, I tried adding the GPU with QEMU command line, but it showed permission denied errors that I couldn't figure out how to solve (even chmod-ing the device to 777 didn't work). I have no idea about the validity of this claim or the supposed solution, so take this with a grain of salt.
Native supported added in KB5017361? - Allegedly some time late 2022, Microsoft added native support for UEFI in Windows 7, supposedly rendering UefiSeven obsolete. Official support would definitely be preferable over "hacks", but the only evidence that this actually happened seems to be a single tweet, it's never been mentioned in any official statement/capacity, and even the tweet makes some mention of workarounds still being required for graphics. Regardless, I installed the update (and all of the updates leading up to it), but experienced no change.
What Worked (For Me)
At first, SeaBIOS + passthrough doesn't appear to work at all, which seemed to reinforce all the people who claimed it was problematic. My passed through GPU displayed no image whatsoever. Regardless, after all the frustration I had with UEFI, the handful of people saying SeaBIOS worked fine for them made me want to try it myself again.
It turns out there was one thing I was missing when trying to passthrough with SeaBIOS. You have to enable x-vga. For some reason I'd never come across this before, but this was the magic flag that made my GPU get picked up by the BIOS and subsequently Windows 7.
As aforementioned, I've read that Windows 7 is too picky for both a real and virtual GPU to be connected, and this does seem to be somewhat correct. At least, when I have both connected, the real GPU gives me "Code 12" for lack of resources again. But with just the real GPU connected (and drivers installed of course), I get perfect fully GPU accelerated Windows 7, in all of its Aero glory.
So once again assuming you have some knowledge of virt-manager/GPU passthrough already (if not, you probably want to use this guide to start from scratch), here's how I got a Windows 7 VM to work:
- Create a virtual machine with virt-manager - there's an existing preset for "win7", from this it should automatically create a Q35 chipset with SeaBIOS which is what you want.
- Check "Customize hardware before install" before finishing the wizard so we can make some changes.
- virt-manager will have made a Display and Video device. To avoid "Code 12" (insufficient resources), you'll need to remove the virtualized GPU by setting the Video device to "None". Alternatively you can remove the Display and Video devices altogether. This can be done at any time, so if you'd prefer to do installation with a vGPU and switch to a real one later, go for it.
- Add your GPU as a PCI Host Device as normal.
- Enable x-vga. This is a QEMU-specific thing, so to do this in virt-manager, you'll want to add some custom QEMU args to the XML:
- Replace the top line with this to allow QEMU command line extensions:
Code: Select all
<domain xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0" type="kvm">
- Add the following lines to the end of the file after "</devices>" but before "</domain>":
Code: Select all
<qemu:override> <qemu:device alias="hostdev0"> <qemu:frontend> <qemu:property name="x-vga" type="bool" value="true"/> </qemu:frontend> </qemu:device> </qemu:override>
- Replace the top line with this to allow QEMU command line extensions:
- That's it! If everything went well, you should now get SeaBIOS running through your GPU. From here, you can proceed to installing and running Windows 7, and finally installing the GPU drivers. For reference, the GPU I passed through was an AMD RX 570, which has native support for Windows 7. If your GPU is too new to have drivers for 7, it will still "work", but without any hardware acceleration, which kind of defeats the purpose of passing through a real GPU to begin with.
I've also heard that NVIDIA cards try to detect if they're being passed through and disable themselves if so (in order to upsell you to their more expensive "enterprise" cards that support it officially). To bypass this, you'll need to configure your VM to hide the fact that it's a VM. There are ways to do this, but it's out of scope for this guide. As always, Google is your friend.