Skip to content

feat(image-recipe): swap U-Boot for EDK2 UEFI on Raspberry Pi#3249

Open
helix-nine wants to merge 3 commits into
masterfrom
helix/raspberrypi-edk2-grub
Open

feat(image-recipe): swap U-Boot for EDK2 UEFI on Raspberry Pi#3249
helix-nine wants to merge 3 commits into
masterfrom
helix/raspberrypi-edk2-grub

Conversation

@helix-nine
Copy link
Copy Markdown
Contributor

@helix-nine helix-nine commented May 20, 2026

Summary

Three layered changes; each commit is reviewable on its own.

1. Swap U-Boot for EDK2 UEFI (feat(image-recipe): swap U-Boot for EDK2 UEFI on Raspberry Pi)

Replaces the firmware → U-Boot → GRUB → kernel chain with firmware → EDK2 UEFI → GRUB → kernel on Raspberry Pi. U-Boot on Pi 5 (BCM2712) can't see PCIe upstream, so NVMe boot doesn't work — see SUSE's Phoronix coverage. After this PR, GRUB on Pi looks the same as on any other arm64 UEFI target.

  • Single image for Pi 4 / 400 / CM4 / Pi 5 / 500 / CM5. config.txt [pi4] / [pi5] sections pick the right RPI_EFI_*.fd per model.
  • Pi 4: pftf/RPi4 v1.51 — Pi Firmware Task Force, builds upstream tianocore/edk2-platforms.
  • Pi 5: NumberOneGit/rpi5-uefi v0.1 — individual-maintainer fork of the now-archived worproject/rpi5-uefi, with D0 silicon support. Pi 5 supply-chain risk documented in build/image-recipe/raspberrypi/README.md.
  • Both EDK2 archives are pulled at image-build time inside the chroot hook (so apt postinst can't clobber them), pinned by version + SHA-256.
  • Dropped: u-boot-rpi, rpi-update from raspberrypi.depends; the RPI_KERNEL_VERSION=6.12.47+rpt pin; the git clone …/rpi-firmware.git step; the cp u-boot.bin step. VPU blobs + kernel both track archive.raspberrypi.com.

2. Ship both Pi kernels, pick via EDK2 SMBIOS at boot

linux-image-rpi-v8 (Cortex-A72 baseline) and linux-image-rpi-2712 (Cortex-A76 tuned) both installed. New squashfs/etc/grub.d/05_pi_kernel_select script runs at update-grub time and emits a grub.cfg prologue that, at boot, reads SMBIOS product name and points ${default} at the rpi-2712 submenu entry for Pi 5 / 500 / CM5 or rpi-v8 otherwise. Pi 5 hardware gets the A76-tuned kernel; Pi 4 (which would trap on rpi-2712) safely defaults to rpi-v8.

3. Pi image becomes a live installer

Same UX as the x86 .iso. Flash the new .img to a USB stick or microSD, boot the Pi, the live system runs setup mode on port 80, user picks a target disk (SD / USB / NVMe), core/src/os_install/ writes the installed StartOS to that target, reboot from the target. No more flash-the-whole-disk-image install procedure; Pi users now get exactly the same setup-mode flow as x86 users.

  • Image layout drops the root partition entirely: just firmware (128 MiB) + efi (100 MiB) + boot (sized to fit kernel + initrd + grub modules + /live/filesystem.squashfs). No more pre-installed btrfs, no init_resize.sh, no rsync raspberrypi/img/ (that tree is now empty — deleted).
  • core/src/os_install/:
    • gpt.rs adds a 128 MiB FAT32 firmware partition (partition 1) when PLATFORM == raspberrypi, shifting efi/boot/root indices down. Non-Pi platforms unchanged (existing partitioning test still passes).
    • mod.rs mkfs.vfats it, then cp -a /boot/firmware/. from the live media. Pi VPU finds bootcode.bin + RPI_EFI_*.fd on partition 1 of the installed target exactly as on the install media.
    • fstab.template gains an optional firmware line (rendered # N/A on non-Pi platforms).
  • build.sh IMG flow: chroots into the lb-built rootfs to run grub-install --target=arm64-efi + update-grub against the bind-mounted boot partition, then post-processes grub.cfg to swap boot=startos for boot=live components live-media-path=/live.

Test plan

I can't exercise this in helix — no Pi hardware in the build slot. The Rust changes pass cargo check --lib and the existing os_install::gpt::tests::preserve_data_partition_from_0_3_5_1_layout test still passes. Needs verification on real hardware before merge:

  • PLATFORM=raspberrypi make iso (well, .img) completes; the resulting image has RPI_EFI_RPI4.fd + RPI_EFI_RPI5.fd on the firmware partition, /boot/live/filesystem.squashfs on the boot partition, and grub.cfg with boot=live components live-media-path=/live.
  • Image boots on a Pi 4 from microSD: VPU multicoloured splash → EDK2 → GRUB → live kernel → setup mode answers on port 80.
  • Image boots on a Pi 5 from microSD: same chain. EDK2 setup menu shows "Raspberry Pi 5". GRUB picks the rpi-2712 menuentry by default.
  • Image boots on a Pi 5 from USB (Pi EEPROM BOOT_ORDER=0xf41).
  • Pi 5 + NVMe HAT+: EDK2 enumerates PCIe before GRUB; install StartOS to the NVMe via setup mode; reboot → boots from NVMe.
  • Installed-disk grub.cfg has boot=startos (i.e. the live-cmdline sed only ran during image build, not during install).
  • /etc/fstab on the installed disk has the firmware line resolved to the right /dev/... (or PARTUUID).
  • update-grub on the installed system still works (e.g. after a kernel package update).

Pi 5 supply-chain caveat

NumberOneGit/rpi5-uefi is a single-maintainer fork. The pinned .zip is sha256-locked so the existing build keeps working even if upstream disappears, but bumping in the future may require picking a successor fork or mirroring under Start9Labs/. Detail in build/image-recipe/raspberrypi/README.md.

Previous chain (firmware → U-Boot → GRUB → kernel) broke on Pi 5:
Debian's u-boot-rpi is BCM2711-era and mainline U-Boot's BCM2712
support is SD+UART only — no PCIe means no NVMe boot, which is the
canonical Pi 5 deployment.

New chain: firmware → EDK2 UEFI → GRUB → kernel. config.txt picks
the right RPI_EFI_*.fd per board via [pi4] / [pi5] conditional
sections; GRUB then runs identically to other arm64 UEFI targets,
so core/src/os_install/ stays generic.

- Pi 4: pftf/RPi4 v1.51 (Pi Firmware Task Force, builds tianocore
  upstream).
- Pi 5: NumberOneGit/rpi5-uefi v0.1 (individual-maintainer fork of
  the archived worproject/rpi5-uefi; only viable EDK2 today —
  supply-chain risk documented in raspberrypi/README.md).

Single image for both boards: --linux-flavours rpi-v8 (boots on
Pi 4 Cortex-A72 and Pi 5 Cortex-A76; rpi-2712's A76-tuned build
would crash a Pi 4).

Dropped:
- u-boot-rpi + rpi-update from raspberrypi.depends.
- The kernel version pin (RPI_KERNEL_VERSION=6.12.47+rpt). Kernel
  now tracks latest from archive.raspberrypi.com.
- The sideband git clone of raspberrypi/rpi-firmware.git. VPU
  blobs come from the raspi-firmware Debian package on
  archive.raspberrypi.com.
- The manual u-boot.bin copy from /usr/lib/u-boot.
- The manual mkinitramfs calls (no longer needed without the
  version pin; deb postinst handles initrd).

EDK2 binaries are downloaded at image-build time inside the
chroot hook (so apt-postinst can't clobber them), pinned by
version + SHA-256.
Previous version installed only rpi-v8 because rpi-2712 (Cortex-A76
tuned) crashes on Pi 4. That worked but left Pi 5 perf on the table —
unnecessary, since GRUB-EFI has the smbios module on arm64 and both
pftf/RPi4 + NumberOneGit/rpi5-uefi publish SMBIOS type-1 with the
board name.

- Restore --linux-flavours "rpi-v8 rpi-2712".
- New squashfs/etc/grub.d/05_pi_kernel_select: shell script run by
  update-grub that detects which kernels are present and emits a
  grub.cfg prologue. At boot, the prologue reads SMBIOS product
  name and points \${default} at the rpi-2712 submenu entry for
  Pi 5 family ("Raspberry Pi 5", "Raspberry Pi 500", "Raspberry
  Pi Compute Module 5") or rpi-v8 otherwise. No-op if either
  flavour is missing or PLATFORM != raspberrypi.

Same single image, automatic board-appropriate kernel selection.
Same UX as the x86 .iso: flash to USB/SD, boot the Pi, live system
runs setup mode on port 80, user picks target disk, os_install writes
installed StartOS to it, reboot from target.

Image layout drops the root partition entirely:
- firmware (128 MiB, FAT32)  VPU + EDK2 .fd + config.txt + DTBs
- efi      (100 MiB, FAT32)  GRUB-EFI loader
- boot     (~1 GiB, FAT32)   grub modules + kernels + initrds +
                              /live/filesystem.squashfs

No more pre-installed btrfs, no init_resize.sh, no rsync of
raspberrypi/img/ (now empty — deleted). grub.cfg generated by
chrooting into the lb-built rootfs and running grub-install +
update-grub against the bind-mounted boot partition; post-process
swaps boot=startos for boot=live.

os_install learns the Pi firmware partition:
- gpt.rs adds a 128 MiB FAT32 partition (partition 1) when
  PLATFORM == raspberrypi, shifting efi/boot/root indices down.
  Non-Pi platforms unchanged (existing test passes).
- mod.rs mkfs.vfats it, then `cp -a /boot/firmware/.` from the live
  media. Pi VPU finds bootcode.bin + RPI_EFI_*.fd on partition 1 of
  the installed target exactly as on the install media.
- fstab.template gains an optional firmware line (rendered # N/A on
  non-Pi platforms).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant