Booting Debian from a BTRFS subvolume from the grub menu in QEMU

I recently had to fix a broken Debian install, and I wanted to test the install in QEMU before puting the disk back into the headless machine it belonged in. Grub was a bit broken, so I had to run some commands manually to get it booted. Here's what worked for me.

Scenario

The broken install is on a SATA disk, and is available on the host machine as /dev/sdb. The disk has a GPT partition table and 3 partitions. The partitions are 1) EFI System, 2) Linux filesystem, 3) Linux Swap

The Linux filesystem is formatted with btrfs. The btrfs system has several subvolumes, including one named

@
which is our root directory.

Booting an UEFI system with QEMU

I didn't have QEMU on this particular Debian system, so I installed it with

sudo apt install qemu-system
.

To try to boot the system, I used the following command:

sudo qemu-system-amd64 -enable-kvm -m 1024 -bios /usr/share/ovmf/OVMF.fd -drive format=raw,file=/dev/sdb
Here's what the arguments mean:
-enable-kvm
Ensures that the KVM is used which passes through to the actual CPU for faster speeds
-m 1024
Use 1024 MB of RAM from the host system
-bios /usr/share/ovmf/OVMF.fd
Use a specific UEFI firmware instead of a classic BIOS. Our install on /dev/sdb isn't configured to boot from a BIOS. Without this we get stickc with a message "Booting from Hard Disk"
-drive format=raw,file=/dev/sdb
Boot from a raw device - treat the attached SATA drive a /dev/sdb like an actual SATA drive. QEMU boots from images by default, this lets us use our actual drive.

Starting with GRUB

My system would boot into the GRUB shell, but further. The first thing I had to find was which partition had my root data. I used the ls command to explore the available drives

 
		>ls 
		(proc) (memdisk) (hd0) (hd0,gpt3) (hd0,gpt2) (hd0,gpt1) (cd0)
		> ls (hd0,gpt2)/     <-- Note the trailing slash to list the contents of directory /
		snapshots-root/ @/ @home/ snapshots-home/
		> ls (hd0,gpt2)/@
		dev/ run/ boot/ lib sbin srv/ media/ usr/ opt/ initrd.img vmlinuz lib64 bin/ home/ etc/ var/ tmp/ initrd.img.old sys/ mnt/ vmlinuz.old proc/ root/
		

Great! So we have found our root filesystem. To make things a little confusing we are referencing this disk in a couple different ways:

Now we need to set the root disk, define the path to the root filesystem, define the initrd to use and boot the system. Since we are using a btrfs subvolume, we need to include those settings here.

			# Set the root device for GRUB. This defines where linux and initrd will be found.
			set root=(hd0,gpt2)

			# Define the kernel to use. The kernel needs to know which device is root, and here we define which btrfs subvolume to use.
			# Our subvol's name is '@' 
			linux /@/vmlinuz root=/dev/sda2 rw rootflags=subvol=@

			# Define the initrd to use
			initrd /@/initrd.img

			# Boot the system
			boot
		

Once I was into the system I made other repairs, but also fixed GRUB by running:

		grub-install
		update-grub
		
Once I fixed the other issues the system was having, I was able to reboot and GRUB worked how it's supposed to from then on.

Hopefully this has been helpful. Good luck!