A Weird Imagination

Virtualizing your own computer

Posted in

The problem#

It's annoying to test boot sequences, either of flash drives or your computer. Rebooting every time you want to test a change is slow and loses your place. Which is especially frustrating if you're using a live USB to attempt to repair a broken bootloader and you have to try multiple times. For a bootable flash drive, at least you might have another computer to test it on, making the problem just mildly inconvenient. But what if you didn't have to boot your computer to test the boot sequence?

The solution#

Of course, you have to boot some computer. But it doesn't have to be a physical one: it can be a virtual machine. While virtual machines often use virtual hard disks backed by files, they can also use real disks. And QEMU's -snapshot flag lets you read from a real disk without actually writing back to it. All changes are stored in temporary storage unless you "commit" them, which you do not want to do for this purpose.

IMPORTANT: Always make sure to use -snapshot when running QEMU on a real disk, especially if that disk is in use by the host system. The following commands also have you set the file permissions so QEMU does not have write access to the disks to be extra careful.

The following will run a virtual machine booting off a flash drive (change /dev/sdc to the appropriate device):

# Grant current user read-only access to flash drive
sudo chgrp "$(id -gn)" /dev/sdc
sudo chmod g=r /dev/sdc
# Boot VM off flash drive
qemu-system-x86_64 -snapshot \
    -net none -machine q35 \
    -bios /usr/share/ovmf/OVMF.fd \
    -cpu host -m 8G -enable-kvm \
    /dev/sdc

To instead boot off your machine's internal drive (assuming it is an NVME SSD with device name /dev/nvme0n1):

uefivars -i efivarfs -o edk2 \
    -I /sys/firmware/efi/efivars -O OVMF_VARS.fd
# Grant current user read-only access to primary SSD
sudo chgrp "$(id -gn)" /dev/nvme0n1
sudo chmod g=r /dev/nvme0n1
# Boot VM off primary SSD
qemu-system-x86_64 -snapshot \
    -net none \
    -drive file=/dev/nvme0n1,if=none,id=nvm \
    -device nvme,serial=deadbeef,drive=nvm \
    -machine q35 \
    -drive if=pflash,format=raw,unit=0,readonly=on,\
file=/usr/share/OVMF/OVMF_CODE_4M.fd \
    -drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd \
    -cpu host -m 8G -enable-kvm

Those commands require QEMU, KVM, and OVMF installed as well as the Python package uefivars. The paths for OVMF are where Debian installed the files on my system, but they may be in a different place or have slightly different filenames on your system.

Note booting the VM may not be instant: that second command takes almost a minute to reach the GRUB menu on my computer.

The details#

Basic QEMU invocation#

The most basic invocation of QEMU is to just give it a disk image:

qemu-system-x86_64 disk.img

As mentioned above, we want to make sure to use the -snapshot flag to not accidentally write to the disk. Additionally, I included -net none to also make sure there's no side-effects on the network either as QEMU attaches VMs to the network by default; that also prevents it from wasting its time trying PXE network booting.

You may need to add the -display gtk option or possibly install the qemu-system-gui package (or your distribution's equivalent) if QEMU does not display the emulated computer's screen or tells you to connect via VNC to see it.

Performance options#

That last line -cpu host -m 8G -enable-kvm makes sure you aren't emulating an underpowered computer. -m 8G gives it 8 GiB of memory since the default of 128 MiB may be insufficient. -cpu host tells it to "emulate" the same processor your computer actually has. -enable-kvm has it use KVM to use your CPU's virtualization support instead of actually acting as an emulator which would be much slower.

NVME emulation#

If you have an NVME SSD, in order to make the virtual machine look enough like your machine to be able to boot, it may be insufficient to just point QEMU at the block device as works for booting off a flash drive or a SATA disk. QEMU supports NVME emulation. The command I gave above uses the example from that documentation on the simplest way to use it.

EFI emulation#

If you try to use just what was described above, you may find that you are able to virtualize your machine booting a flash drive but not booting itself. The problem is that QEMU does not default to EFI boot, while modern computers do. The fix is to use OVMF which provides the firmware for an EFI machine along with the -machine q35 option which tells QEMU to emulate a machine that supports that firmware. On Debian, you can install the ovmf package and then provide QEMU with -bios /usr/share/ovmf/OVMF.fd.

EFI booting flash drives#

Note that flash drives can boot in EFI mode, they just often support legacy BIOS mode. One confusion I ran into using the multiboot USB loader Ventoy is that it supports both, but when booting in legacy BIOS mode, it, naturally, can't load .efi images. Since it dynamically generates the boot menu with the items it can find, that means that those items are just silently omitted from the list when booted in legacy BIOS mode but appear when booting via EFI.

EFI vars#

In order to accurately emulate your machine, you also need your machine's EFI configuration, which can be found in /sys/firmware/efi/efivars. OVMF supports configurable EFI vars, and there's a tool uefivars that will convert EFI vars between different formats including the one OVMF uses.

The Python package is not in Debian, so I had to install it separately:

python -m venv ./venv
. ./venv/bin/activate
pip install uefivars

Then you can use it to create the OVMF_VARS.fd file:

uefivars -i efivarfs -o edk2 \
    -I /sys/firmware/efi/efivars -O OVMF_VARS.fd

Since the firmware is now in two separate files, we can't use the -bios shortcut to pass it into QEMU. Also, we need to use the code-only OVMF image since we're providing our own vars:

    -drive if=pflash,format=raw,unit=0,readonly=on,\
file=/usr/share/OVMF/OVMF_CODE_4M.fd \
    -drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd \

I got those options from this blog post, which goes into great detail on exactly what is going on with QEMU and EFI.

Comments

Have something to add? Post a comment by sending an email to comments@aweirdimagination.net. You may use Markdown for formatting.

There are no comments yet.