Home Other Booting Linux from a VHD on a UEFI computer

Booting Linux from a VHD on a UEFI computer

by admin

In an article about the possibility of to boot Linux from a VHD article offered a way to boot Linux on a Windows machine without having to partition the disk.But there was one major limitation:only BIOS booting was considered, i.e. legacy booting. Nowadays there are more and more devices which do not support legacy mode (e.g. many laptops released in 2020). So, in this article we will boot Linux from VHD on computers with UEFI.

The differences between UEFI and BIOS are described quite extensively on the Internet and can be found at this article Most important for us is to use GPT partitioning for the boot drive and when creating VHD partitions. The experiments were done with Secure Boot turned off. I will not go into the details of the in the previous article As before, it is assumed that the reader is familiar with the console in Windows and Linux, knows how to work with the standard system utilities, virtualization software, etc.

I will note a few other points in advance. We could consider booting the system from the computer’s internal hard drive, but we will make things a bit more complicated and boot the system from a removable USB drive which will contain the VHD file. All steps are also applicable to the internal hard disk and some peculiarities will be noted in a separate section.

The developers of grub4dos recently released a version of their UEFI boot loader Using the grub4dos-for_UEFI-2021-02-10.7z version on a virtual machine, I had no problem booting Linux from VHD, but on a real machine I got an error : Error 24: Attempt to access block outside of partition. Due to the ease with which UEFI allows to replace the boot loader (a simple file swap) I decided to use the grub2 boot loader from Debian debian-10.8.0-amd64-netinst.iso.

Installing Linux on a VHD

There are two key differences from BIOS option. :

1) In the VirtualBox settings, you must set the "Enable EFI" option;

VirtualBox settings
Booting Linux from a VHD on a UEFI computer

Other partitioning options are also possible. The ESP partition can be moved out of the VHD if you want (we need it only for booting in the virtual machine), but all further settings are based on the fact that there will be two partitions in the VHD.

Preparing Linux to boot from a VHD

Script from previous article suits us for booting on a local computer where the composition of the disks rarely changes and we know beforehand on which disk the VHD-file is stored. However, we have set ourselves a new goal: to be able to boot from a USB key on any computer with any configuration of hard disks. The grub4dos and grub2 boot loaders can look for files on accessible partitions and work with Partition UUID We will use this when we create the boot loader configuration file, but for now let’s add the UUID handling to the loop_boot_vhdscript.

#!/bin/shPREREQ=""prereqs(){echo "$PREREQ"}case $1 in# get pre-requisitesprereqs)prereqsexit 0;;esaccmdline=$(cat /proc/cmdline)for x in $cmdline; dovalue=${x#*=}case ${x}inroot=*) value_loop_check=${value#*loop}if [ x$value_loop_check != x$value ]then loop_dev=${value%p*}loop_part_num=${value##*p}else echo "Root device is not a loop device. loopboot hook will be terminated."; returnfi ;;loop_file_path=*) loop_file_path=$value ;;loop_dev_path=*) loop_dev_path=$value ;;loop_dev_uuid=*) loop_dev_uuid=$value ;;esacdoneif [ -z $loop_file_path ] || ([ -z $loop_dev_path] [ -z $loop_dev_uuid])then echo "Either loop_file_path or loop_dev_path/loop_dev_uuidparameter does not specified. loopboot hook will be terminated."; returnfimodprobe loop max_part=64 max_loop=8for n in 1 2 3 4 5 6 7 8 9 10; doif [ ! -z $loop_dev_uuid ]; thenloop_dev_path=$(findfs UUID=$loop_dev_uuid)fitest_usb_ready=$(ls $loop_dev_path 2> 1)if [ "$loop_dev_path" != "$test_usb_ready" ]; thenecho "wait for $loop_dev_path ($loop_dev_uuid): $n"sleep 1elsebreakfidoneloop_dev_type=$(blkid -s TYPE -o value $loop_dev_path)if [ ! -d /host ]; thenmkdir /hostfiif [ "$loop_dev_type" != "ntfs" ]then mount -t $loop_dev_type $loop_dev_path /host; echo "mount -t $loop_dev_type $loop_dev_path /host"; echo "mounted using mount";else ntfs-3g $loop_dev_path /host; echo "mounted using ntfs-3g";filosetup $loop_dev /host$loop_file_path

New parameter reading added to the script loop_dev_uuid It expects UUID of the partition where the VHD file is located (the path to it is passed, as before, in the loop_file_path parameter ). If it is given loop_dev_uuid , the value passed in loop_dev_path is ignored and replaced by the UUID found for the device. Also, if the disk is connected via USB, it may be detected with a delay in the system. That is why we added the wait time (up to 10 seconds) during which we run a one second check if the required device is detected or not. We search for the partition by UUID using the findfs utility. It is not included into the initramfs image, to add it you have to create the script /etc/initramfs-tools/hooks/copyfindfs(don’t forget to make the scripts executable).

#!/bin/shcp -p /usr/sbin/findfs "$DESTDIR/bin/findfs"

The loop_boot_vhd script must be located, as before , in /etc/initramfs-tools/scripts/local-top/. After that, rebuild initramfs, and VHD image can be considered ready.

update-initramfs -c -k all

Setting grub.cfg

In GRUB2 you can get the UUID of a partition with the probe module, so it has to be in the build. Based on the GRUB2 build from the Debian installation disk, we need the following files to load :


They must be copied to the EFI partition of the USB drive :


Next, you need to create a configuration file EFIdebiangrub.cfg.

menuentry "vhdUUID" {insmod probeset vhd_name="/debefi.vhd"search --no-floppy --set=vhd_dev --file $vhd_nameprobe -u $vhd_dev --set=vhd_uuidloopback loop ($vhd_dev)$vhd_namelinux (loop, gpt2)/boot/vmlinuz-4.19.0-14-amd64 root=/dev/loop0p2 rw loop_file_path=$vhd_name loop_dev_uuid=$vhd_uuidinitrd (loop, gpt2)/boot/initrd.img-4.19.0-14-amd64}

In the cfg-file everything is quite obvious, just note that the VHD-file has the name debefi.vhd, and the system will search for it in the root of all found partitions. To avoid errors, the file name should be unique for the booted system. Well "gpt2" is used because Linux is installed on the second partition inside the VHD.

Features a parallel setup with Windows bootloader

Disclaimer: Further actions may lead to inability to boot your computer by normal means, all actions you perform at your own risk. Before you start an experiment check if the installed antivirus software controls the integrity of the boot loader. And do not forget about possible side effects

I will describe one way to implement a choice between Windows and Linux booting: by replacing the bootloader with GRUB2. EFI systems start with EFIBootBOOTX64.EFI by default, Windows 10 uses EFIMicrosoftBootbootmgfw.efi. You can replace the Microsoft boot loader with GRUB2 simply by replacing the file. You need to be careful, if you doubt the result the first time, it is better to experiment on a virtual machine first. Use diskpart.exe to mount the Windows EFI partition where you need to :

  • Rename/transfer the file EFIMicrosoftBootbootbootmgfw.efi to EFIbootms.efi;

  • Rename the bootx64.efi file from the Debian ISO image to EFIMicrosoftBootbootmgfw.efi;

  • Place grubx64.efi in EFIMicrosoftBootgrubx64.efi;

  • place probe.mod in EFIdebianx86_64-efiprobe.mod;

  • Copy the previously created grub.cfgto EFIdebiangrub.cfg and add a clause to transfer control to the Microsoft boot loader:

menuentry "ms" {chainloader /EFI/boot/ms.efi}

Now when booting, the GRUB2 menu will appear first with a choice to boot Windows ("ms") or Linux ("vhdUUID").

If errors occur at any stage of the boot, then ( as with grub4dos ) you will have to try to enter commands manually and see what kind of errors you get, how and where partitions are mounted, if all needed files are available, etc.

You may also like