User Tools

Site Tools


grubpxemultiboot

GRUB: PXE-booting into specific OS on multiboot systems

Hey, my name is "Tux"! Did you ever wonder how you can boot automatically into a specific OS via PXE-boot on a multiboot system?

Hey, my name is "Tux" and in this tutorial I will show you an example on how to boot automatically into a specific OS via PXE-boot on a dualboot system based on Linux and Windows.



Start of tutorial

Note

This tutorial is based on x86_64 systems.

The appliances/systems being used in this tutorial are:
  • DHCP server: ISC DHCPv4 server service (Provided natively by OPNsense 25.7.1_1-amd64).
  • TFTP server: Synology TFTP server (Provided natively by Synology DSM 7.2.2-72806 Update 4).
  • Bootloader: GRUB 2.12 bootloader (Optional: Built on a Ubuntu server 24.04 VM. The VM is just needed temporarily to build a custom GRUB bootloader, after that, the VM can be deleted).
  • Dualboot computer: This is the target computer. In this example it has a custom Linux (Buildroot) OS and Windows 11 OS installed. Make sure Secure boot is disabled in BIOS/UEFI settings unless you plan to digitally sign the custom GRUB bootloader file (which is not part of this tutorial) we are going to generate in this tutorial or alternatively use an already signed GRUB bootloader out of an existing Linux installation (There are chances that it may not work because GRUB modules are missing, that's why we are building our own GRUB bootloader in this tutorial.).

Dualboot computer

First thoughts

Typically, every existing OS brings its own dedicated bootloader partition with it. This is mostly done automatically when the OS gets installed. From a compatibility perspective in this tutorial it does not really matter whether the OSes are installed on the same physical disk, you can indeed have multiple bootloader partitions installed on the same physical disk. But it is strongly recommended to have every OS on its dedicated physical disk installed because chances are much better to not run into any unexpected issues and you will be much more flexible in the future. You have been warned!

Boot partition UUIDs

First of all, on your dualboot target computer, it is necessary to determine the UUIDs of the according boot partitions. My recommended way to do this is to use Linux. So, if one of your installed OSes is Linux, boot into Linux now. If not, you could alternatively create a temporary bootable Linux USB stick (e.g. Debian) and boot from the USB stick, which is not part of this tutorial.
Once on the Linux CLI, let's determine the according boot partitions by executing the following command, which lists up all the existing disk partitions and filters them for typical keywords indicating the boot partitions (e.g. EFI, boot, vfat):

Note

Please be aware that anything in Linux is case sensitive and boot partition setups can vary between Linux distributions, e.g. boot partition keywords and where the boot partitions are mounted. The values being used in this tutorial are just a working example out of my personal setup, they may or may not be the same in your setup. Typical keywords could be:
  • EFI
  • efi
  • boot
  • Boot
  • BOOT
  • vfat
findmnt -o UUID,SOURCE,FSTYPE,TARGET,PARTLABEL | grep 'EFI\|boot'

The output should be something similiar to the following:

264A-F71E     /dev/nvme0n1p1     vfat     |-/boot                 vfat
D259-CAE5     /dev/nvme2n1p1     vfat     | |-/media/NO_LABEL     EFI system partition

The output is split up into 5 rows (from left to right) which should help you to determine the according boot partitions:

  • UUID: These are the values we are looking for. To make sure the listed UUIDs are really the according boot partitions, have a look on all the additional rows:
  • SOURCE: Represents the physical hard drive(s) the partition is installed on. Check if it makes sense.
  • FSTYPE: The partition's filesystem type, mostly vfat for boot partitions.
  • TARGET: The partition's mount point. Check if it makes sense by searching for typical boot partition files or file structures by navigating through the filesystem, especially on Linux (e.g. /boot/EFI/bootx64.efi). For Windows, the EFI boot partition with its bootloader file is always the same: /EFI/Microsoft/Boot/bootmgfw.efi, so just look out for that.
  • PARTLABEL: Shows the partition's label. Check if it makes sense.

Now if the boot partitions are determined, write them down somewhere, they are needed on the following steps. Furthermore write down where the according .efi-files are located at, these are the bootloader files needed for chainload configuration later.

TFTP server

In this tutorial the Synology DSM's native TFTP server is being used. To set it up accordingly, do as follows:

  • Login to Synology DSM's web interface.
  • Create a new shared folder called tftpboot:
    Control PanelShared FolderCreateCreate Shared Folder → On Name insert tftpbootNextNextNextNext → Leave default permissions, click Apply.
  • Enable TFTP service:
    Control PanelFile ServicesAdvancedTFTP → Tick Enable TFTP service → Click on Select and select the according tftpboot shared folder → SelectAdvanced SettingsPrivileges setup → Set TFTP client permission: to WriteableSaveApply.

That's it. You have the TFTP server up an running and if you have the according SMB service enabled and set up accordingly, you can access it additionally via SMB protocol to transfer your files to later, which is not necessarily required but recommended.

Note

Don't forget to set the according firewall rules on all the firewalls involved on the according network chain (e.g. OPNsense firewall, Synology's native firewall, …). Please note that for this initial TFTP setup it is only necessary to let your dualboot computer access the TFTP server share via TFTP protocol (Default: Port 69/UDP), the DHCP server itself (e.g. OPNsense) does not need to access the TFTP server in any way, it just passes the information about the TFTP server via DHCP protocol to the dualboot computer.

Also note, that for the TFTP share itself you don't need to set any additional share permissions. The TFTP protocol is very simple and primitive and has no real advanced security features (e.g. authentication), the Synology TFTP service handles the special TFTP access permissions itself. It is intended to be used for "simple" purposes so it is a good idea to keep it in an isolated environment from a (network) security perspective.

GRUB

GRUB bootloader binary file

Now it is required to get the GRUB bootloader binary file which is the GRUB bootloader executable itself. You can either download the working one provided by me or build it by yourself:

  • Option 1: Download the GRUB bootloader binary file from here:
    grubx64.zip
  • Option 2: Build the GRUB bootloader binary by yourself: Fire up the Linux computer where you want to create the according GRUB bootloader binary file. In this tutorial an Ubuntu server VM is being used for this, but could be any common Linux distribution or also the Linux distribution installed on your dualboot computer. For Ubuntu, install the required dependencies by executing the following CLI command with higher privileges:

    sudo apt-get install grub-common grub-efi-amd64-bin grub-efi-ia32-bin

    To create the GRUB bootloader binary including the required modules, execute (Don't change anything!):

    cd ~ && sudo grub-mkimage -o grubx64.efi -p "(tftp)/" -O x86_64-efi tftp configfile linux normal chain boot echo search search_fs_uuid efinet efi_gop efi_uga gfxterm part_gpt ntfs net fat part_msdos ext2 test help cat

    The according grubx64.efi was being created here:
    ~/grubx64.efi
    Move that file over to the TFTP server share.

GRUB bootloader config files

On execution the GRUB bootloader binary looks automatically for a config file called grub.cfg. In addition to that file we will create a flag file, which only holds the information about which OS to boot from (e.g. Linux or Windows).

Sidenote

Generally, the GRUB bootloader handles .cfg-files as config files, meaning the boot process reads and executes .cfg-files like parts of a script in its own scripting language. So as long as .cgf file extensions are being used, those script files can be split up into multiple files.
.cfg flag file

On the TFTP server share, create a file called boot_target.cfg, with the following content (We just set Windows to default for now, you'll be able to change this dynamically later with your custom setup as mentioned on the end of this tutorial.):

set boot_target=windows
.cfg main file

In addition to that file, create another file called grub.cfg on the TFTP server share, with the following content (Replace the according placeholders (e.g. <windowsBootPartitionUUID>, …) with your custom values:

Note

For the following config file, you always need to think from GRUB's early stage perspective when it comes into path configurations. For example, it is important to understand that for the GRUB chainloader binary file's path on Linux boot partitions, it is required to set the path to /EFI/boot/bootx64.efi instead of /boot/EFI/boot/bootx64.efi, which is a relative path instead of the absolute full path.
set timeout=2
source (tftp)/boot_target.cfg
if [ "$boot_target" = "windows" ] ; then
    menuentry "Windows" {
        insmod part_gpt
        search --no-floppy --fs-uuid --set=root <windowsBootPartitionUUID>
        chainloader <windowsBootLoader>.efi
    }
elif [ "$boot_target" = "linux" ] ; then
    menuentry "Linux" {
        insmod part_gpt
        search --no-floppy --fs-uuid --set=root <linuxBootPartitionUUID>
        chainloader <linuxBootLoader>.efi
    }
fi

The following is my productive working example:

set timeout=2
source (tftp)/boot_target.cfg
if [ "$boot_target" = "windows" ] ; then
    menuentry "Windows" {
        insmod part_gpt
        search --no-floppy --fs-uuid --set=root D259-CAE5
        chainloader /EFI/Microsoft/Boot/bootmgfw.efi
    }
elif [ "$boot_target" = "linux" ] ; then
    menuentry "Linux" {
        insmod part_gpt
        search --no-floppy --fs-uuid --set=root 264A-F71E
        chainloader /EFI/boot/bootx64.efi
    }
fi

UEFI/BIOS settings

Now make sure that PXE-boot is enabled on your BIOS/UEFI settings. The way to enable PXE-boot mostly differs by any mainboard type, so this will not be covered in this tutorial. Just look out for a setting like "Network boot" or "PXE-boot". When enabled, you typically can choose a network interface to boot from. If your have multiple ones, take the one that you want to boot from (mostly dependent on your network setup). Once enabled, set PXE-boot option as the first one in the BIOS/UEFI boot order settings.

DHCP server

You will need a PXE-boot capable DHCP server, which basically means, it needs to be capable of providing network booting information to PXE-boot client computers (e.g. your dualboot computer). In this tutorial the according DHCP server is provided natively by OPNsense router/firewall. OPNsense does provide multiple DHCP server services, in this tutorial the ISC DHCPv4 server service option is being used. It is assumed you have already set up the basic DHCP service, this tutorial only covers on how to enable network boot to make PXE-boot possible. On the OPNsense web interface GUI navigate to (Replace <NetworkInterface> with the logical network interface or VLAN interface your dualboot computer's network interface which should boot off of PXE):

ServicesISC DHCPv4<NetworkInterface>Network bootingAdvanced → Tick Enable network booting → On Set next-server IP input field insert the TFTP server's IP address (e.g. 192.168.1.100) → On Set x64 UEFI/EBC (64-bit) filename input field insert the according GRUB binary's filename (e.g. grubx64.efi) → Save

That's it for the basic setup!
Everything is set up to boot via PXE into a specific OS on a dualboot computer.

Remote system(s)

Now you may still wonder on how to set the according options to decide which OS to boot from. Well, for that there are many possibilities, that's why it does not make much sense to cover more here and this is why this tutorial is abstracted from here on.
The important thing to understand is: The main file where GRUB is looking into on PXE-boot stage is the boot_target.cfg flag file. We have initially set it up by default to boot into the Windows boot partition. If you want to switch booting between boot partitions dynamically, the according value has to be changed inside the file (e.g. from set boot_target=windows to set boot_target=linux and vice versa). This typically is done via a remote computer that can access the TFTP server share either via TFTP, SMB, NFS or SPC/SSH protocol.
In my case I did this via a Home Assistant OS VM and a Debian VM as follows:

In case of an according event (e.g. a physical button press on a smart device), the Home Assistant VM connects to the Debian VM via SSH and updates the boot_target.cfg flag file's content (the file's full path on the Debian VM is: /home/user/tftpboot/boot_target.cfg). Then, the Debian VM sends/overwrites that file via TFTP protocol to the Synology TFTP server share. All this is done via a single shell command, here's an example of the two according Home Assistant Shell Command integration commands I use to switch between Windows and Linux boot partitions:

shell_command:
  # desktop01_tftpboot:
  pc1_tftpboot_windows: ssh -i /config/.ssh/id_rsa -o 'StrictHostKeyChecking=no' user@debian01.local 'echo "set boot_target=windows" > /home/user/tftpboot/boot_target.cfg && cd /home/user/tftpboot && tftp synology01.local -c put boot_target.cfg boot_target.cfg'> /dev/null 2>&1 &
  pc1_tftpboot_linux: ssh -i /config/.ssh/id_rsa -o 'StrictHostKeyChecking=no' user@debian01.local 'echo "set boot_target=linux" > /home/user/tftpboot/boot_target.cfg && cd /home/user/tftpboot && tftp synology01.local -c put boot_target.cfg boot_target.cfg'> /dev/null 2>&1 &

By using this two commands, it is possible to create automations in Home Assistant depending on how it is required to use them. In my case, this works like a charm!

There's a ton of possibilities on how you can change the content of the boot_target.cfg flag file remotely, you'll get the idea! Just keep in mind to set all the according required firewall rules between all firewalls involved in the according network chain, depending on the network services/protocols you want to use. Also, if you are changing the content of the boot_target.cfg flag file remotely only via SSH protocol and not via TFTP protocol, you may also set the according permissions to Read only on the Synology TFTP server service settings (Remember, we set it to Writeable before.).

End of tutorial



Appreciate my work?
Buy me a coffee or PayPal

grubpxemultiboot.txt · Last modified: