Booting Ubuntu live ISOs via U-Boot
U-Boot can replace GRUB as the bootloader on an Ubuntu live ISO.
scripts/ubuntu-iso-to-uboot.py rewrites the ISO so the appended EFI system
partition holds a U-Boot EFI application and the ISO 9660 tree carries
a Boot Loader Specification Type #1 entry pointing at the
casper kernel and initrd that Ubuntu already places on the disc.
All other boot records (BIOS El Torito, grub2 MBR, GPT layout) are preserved
verbatim by xorriso’s -boot_image any replay, and casper still finds its
squashfs by disc label at runtime.
Host prerequisites
sudo apt install xorriso mtools dosfstools squashfs-tools fakeroot \\
qemu-system-x86 ovmf
squashfs-tools and fakeroot are only needed when the script modifies the
install squashfs (see Keeping BLS entries fresh after install); pass -N to skip that step
if you’d rather not install them.
Building the U-Boot EFI application
The efi-x86_app64 target enables CONFIG_BOOTMETH_BLS=y,
CONFIG_FS_ISOFS=y and CONFIG_JOLIET=y by default, so no Kconfig tweaks
are required. Build with:
make O=/tmp/b/efi-x86_app64 efi-x86_app64_defconfig
make O=/tmp/b/efi-x86_app64 -j$(nproc)
The output is /tmp/b/efi-x86_app64/u-boot-app.efi, a PE32+ x86_64 EFI
application.
If rustc is not installed, also disable the rust example build before the
main make:
scripts/config --file /tmp/b/efi-x86_app64/.config \\
-d RUST_EXAMPLES -d EXAMPLES
make O=/tmp/b/efi-x86_app64 olddefconfig
Rewriting an Ubuntu ISO
Fetch the ISO (desktop and server images both work) and run the helper:
curl -LO https://releases.ubuntu.com/24.04.1/ubuntu-24.04.1-desktop-amd64.iso
scripts/ubuntu-iso-to-uboot.py ubuntu-24.04.1-desktop-amd64.iso \\
-u /tmp/b/efi-x86_app64/u-boot-app.efi \\
-o ubuntu-uboot.iso
The script:
Reads the input ISO’s boot record with
xorriso -report_el_toritoto pick up the volume label and the EFI system partition GUID, and parses/boot/grub/grub.cfgto inherit the kernel command line from the source ISO’s own default entry (--- quiet splashon Ubuntu 24.04.1).Builds a small FAT ESP (4 MiB by default) containing just
/EFI/BOOT/BOOTX64.EFI– the U-Boot EFI application.Writes a new ISO with
xorriso -indev ... -outdev ... -boot_image any replay -append_partition 2 ... -map entry.conf /loader/entry.conf, which replaces the original ESP and adds/loader/entry.confto the ISO 9660 tree. The kernel and initrd stay in/casper/on the ISO 9660 tree; U-Boot reads them directly via its isofs driver.Strips the shim, GRUB and MOK manager binaries (
/EFI/boot/{bootx64,grubx64,mmx64}.efi) from the ISO 9660 tree. The UEFI firmware loadsBOOTX64.EFIfrom the appended ESP, so the ISO 9660 copies are unused dead weight. The BIOS El Torito image under/boot/grub/is left in place, so legacy-BIOS boot still chains into GRUB as before.Wires BLS maintenance into the target system that a later
Install Ubuntuproduces; see Keeping BLS entries fresh after install.
Relevant options:
-u PATH– the U-Boot EFI application (required).-o PATH– the output ISO (required).-a ARGS– override the kernel command line written toloader/entry.conf. When not given, the arguments after the firstlinux /casper/vmlinuzentry in the source ISO’s/boot/grub/grub.cfgare used verbatim, so the rewritten ISO boots exactly as the original. Pass something likeconsole=ttyS0,115200 console=tty0 --- quiet splashwhen you want the kernel logged to serial too.-k PATH/-i PATH– override the kernel and initrd paths inside the ISO if a distribution uses something other thancasper/vmlinuzandcasper/initrd.-s MiB– force an ESP size; the default is 4 MiB, enough for the U-Boot binary.-t TITLE– override the BLS entry title.-N/--no-target-bls– skip the Keeping BLS entries fresh after install step and produce a plain rewritten ISO. Useful when you do not wantsquashfs-tools/fakerooton the host or cannot afford the extra ~3 minutes the squashfs repack adds.-I PATH/--install-squashfs PATH– path inside the ISO of the install squashfs to modify for interactive installs. Defaults tocasper/minimal.squashfs; set to an empty string to skip the squashfs step while still shipping the autoinstall snippet.-v– show progress markers and subprocess output (xorriso, mksquashfs, mkfs.vfat). Off by default.
Testing under QEMU + OVMF
cp /usr/share/OVMF/OVMF_VARS_4M.fd /tmp/OVMF_VARS.fd
qemu-system-x86_64 -machine q35 -m 4096 -smp 2 -enable-kvm \\
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \\
-drive if=pflash,format=raw,file=/tmp/OVMF_VARS.fd \\
-drive if=virtio,file=ubuntu-uboot.iso,format=raw,readonly=on
The expected boot trace on the serial console is roughly:
U-Boot Concept 2026.02
...
Scanning bootdev 'efi_media_0.bootdev':
0 bls ready pci 1 0 efi_media_0.bootdev.part_ /loader/entry.conf
** Booting bootflow 'efi_media_0.bootdev.part_1' with bls
Retrieving file: /casper/vmlinuz
Retrieving file: /casper/initrd
Linux kernel version 6.8.0-41-generic ... Ubuntu
Starting kernel ...
The bootflow appears on part_1 – the ISO 9660 partition – because
entry.conf lives in the ISO 9660 tree and U-Boot reads kernel and initrd
from the same partition via isofs.
Keeping BLS entries fresh after install
A rewritten live ISO boots via U-Boot + BLS, but once the user clicks
Install Ubuntu, apt-managed kernel updates still go through update-grub
on the target. They do not refresh /boot/loader/entries/, so the installed
system keeps booting the kernel the installer unpacked rather than whatever
apt install linux-image-* lays down later.
By default the script reaches both possible install paths to seed BLS entries for the installer’s kernel:
Autoinstall path.
/autoinstall.yamlat the ISO 9660 root carrieslate-commandsthat, after subiquity has finished copying the system to disk, write a BLS entry per kernel directly to/boot/efi/loader/entries/<v>.confon the install target, copying the kernel and initrd alongside on the ESP so the entry can reference them by ESP-relative paths. A/loader/entry.conffallback is written too, matching the format the live ISO already uses. Plain interactive installs ignore/autoinstall.yamlentirely, so shipping the file is harmless when autoinstall is not requested.The
kernel-installround-trip used by other distros is bypassed: inside curtin’s chroot,/boot/efiis a plain directory rather than the ESP mountpoint, andsystemd’s90-loaderentry.installplugin silently exits when it cannot find a real ESP under$BOOT_ROOT.Entries land on the ESP rather than on
/boot/loader/entries/on the rootfs because the bootflow iterator advances per-partition. On the install target the ESP is the lower-numbered partition; without an entry there the EFI bootmeth would match/EFI/BOOT/BOOTX64.EFIbefore the BLS bootmeth ever reached the rootfs further down, chain-loading U-Boot into itself. Putting the entry on the ESP lets the BLS bootmeth win on that partition before the EFI bootmeth has a turn. A separately maintained/boot/loader/entries/on the rootfs would still be picked up (the BLS bootmeth scans every partition); this just guarantees a working entry on the partition the iterator reaches first.Interactive path. The script unpacks the default install squashfs (
casper/minimal.squashfs), drops in aoneshotsystemd unit plus its helper script and a copy ofu-boot.efiat/usr/lib/u-boot/u-boot.efi, and repacks. On first boot of the installed system the unit runsapt install systemd-boot-efi, back-fills/boot/loader/entries/viakernel-install, and installsu-boot.efionto the target ESP (see Installing U-Boot onto the target ESP below).The unit is gated on
ConditionPathExists=!/cdromso it stays dormant inside the live session, and the done-file stops it re-running on subsequent boots. It needs a network on first boot for the apt call; if that is not available it fails gracefully and retries on the next boot.
Installing U-Boot onto the target ESP
Subiquity writes shim and GRUB to the installed ESP and registers an ubuntu
NVRAM entry pointing at /EFI/ubuntu/shimx64.efi. Without further action,
firmware prefers that entry over the removable-media fallback, so control leaves
U-Boot as soon as the install reboots.
Both install paths therefore plant u-boot.efi on the target ESP at two
locations:
/EFI/BOOT/BOOTX64.EFI– the firmware fallback, picked up when no NVRAM entry matches (for example after a CMOS reset)./EFI/ubuntu/shimx64.efi– the file theubuntuNVRAM entry points at, overwritten in place so the entry boots U-Boot rather than chaining through shim into GRUB.
The autoinstall path does the copy in its late-commands via
curtin in-target; the interactive first-boot unit does it from the running
installed system. Either way u-boot.efi travels through the install inside
the modified squashfs at /usr/lib/u-boot/u-boot.efi, so the installed disk
boots U-Boot + BLS on its own once the live ISO has been ejected.
For interactive installs, every subsequent apt install linux-image-* on the
target updates /boot/loader/entries/ automatically via the
kernel-install hooks, and U-Boot’s BLS bootmeth keeps picking up the current
kernel/initrd without regenerating the ISO. Autoinstall systems ship with one
entry per kernel the installer unpacked but do not auto-refresh on later kernel
updates – the autoinstall path is intended to get the freshly installed system
to its first boot under U-Boot, not to replace kernel-install. Run the
interactive path’s first-boot unit (or kernel-install add manually)
afterwards if you want apt-driven kernel updates to maintain BLS entries on an
autoinstalled system.
Pass -N / --no-target-bls to skip both steps and produce a plain
rewritten ISO. The squashfs repack is the slow part of the build (about three
extra minutes on the 24.04.1 desktop ISO); skipping it keeps the rewrite under
a minute at the cost of needing a manual kernel-install add on the
installed system.
See also
Boot Loader Specification (BLS) – the U-Boot Boot Loader Specification bootmeth.
kernel-install(8)– systemd’s kernel install hook.