Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/GUIDE-IMAGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ Metal images ship with the full set of Linux firmware and driver modules, suppor

Pi images ship the Raspberry-Pi-specific kernel (`linux-raspi`) and firmware (`linux-firmware-raspi`), which together cover the Pi 5's onboard hardware.

### Tryboot rollback (pi)

Pi images use the Raspberry Pi bootloader's A/B "tryboot" mechanism via `flash-kernel-piboot`. Each kernel `apt upgrade` stages the new kernel/initramfs/DTB into `/boot/firmware/new/`; the first reboot trials those assets, and a single failed boot rolls back to the previous known-good set in `/boot/firmware/current/`. A second successful boot promotes the new assets.

This requires Pi 5 EEPROM firmware dated `2025-02-11` or later. New images ship a compatible EEPROM via `just pi-eeprom-img`.

Pi images flashed before this layout was introduced can be migrated in place by SSHing in and running `sudo scripts/migrate-pi-to-piboot.sh` from a checkout of this repo (or copy the script over and run it directly).

## Networking

**cloud-init user-data** is enabled, allowing e.g. cloud images to be configured by cloud providers for networking purposes at first boot.
Expand Down
31 changes: 20 additions & 11 deletions docs/spec/disk-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,24 +200,33 @@ volumes. A mismatch means the system will fail to boot.
> r[image.boot.pi-firmware]
> For the `pi` variant, the bootloader is the Raspberry Pi 5 EEPROM
> firmware. No GRUB is installed. The firmware partition (mounted at
> `/boot/firmware`) must contain `config.txt` selecting `vmlinuz` as the
> kernel and `initrd.img` as the initramfs (`followkernel` mode), and the
> Pi-specific DTB (`bcm2712-rpi-5-b.dtb`) plus its overlays directory.
> `/boot/firmware`) must contain `config.txt`, the Pi-specific DTB
> (`bcm2712-rpi-5-b.dtb`) and its overlays, and a kernel + initramfs
> pair selected by `config.txt`.

r[image.boot.pi-cmdline]
For the `pi` variant, kernel command-line arguments are read from
`/boot/firmware/cmdline.txt`. The cmdline must reference the LUKS-mapped
root device (`root=/dev/mapper/root`) and the BTRFS subvolume
(`subvol=@,compress=zstd:6`).

> r[image.boot.pi-firmware-update]
> The `pi` variant must ship a script that copies the kernel image,
> initramfs and Pi 5 DTB (plus overlays) from their installed locations
> into `/boot/firmware`. This script must run at image build time after
> dracut, and on every kernel package upgrade via a hook in
> `/etc/kernel/postinst.d/`. Without it, kernel updates would leave the
> firmware partition stale and the system would keep booting the old
> kernel after `apt upgrade`.
r[image.boot.pi-firmware-update]
For the `pi` variant, kernel, initramfs, DTB and overlay updates must be
propagated to the firmware partition on every kernel package upgrade,
without operator action. The propagation must not overwrite the running
known-good boot assets — see r[image.boot.pi-tryboot-rollback].

> r[image.boot.pi-tryboot-rollback]
> For the `pi` variant, the firmware partition must implement an A/B
> boot layout: new kernel/initramfs/DTB assets are staged separately
> from the running known-good set, and a single failed boot of the new
> assets must automatically roll back to the previous known-good set on
> the next boot, with no operator intervention. A subsequent successful
> boot of the new assets must promote them to known-good.
>
> The EEPROM firmware must be recent enough to support the trial-boot
> mechanism. On Pi 5 / 500 / CM5 the floor is firmware dated
> `2025-02-11` or later.

r[image.boot.pi-uart]
For the `pi` variant, the kernel console must be available on the Pi 5
Expand Down
42 changes: 27 additions & 15 deletions image/configure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -196,26 +196,36 @@ echo "$VARIANT" > /etc/bes/image-variant
# ============================================================
if [ "$VARIANT" = "pi" ]; then
# r[image.boot.pi-firmware] r[image.boot.pi-cmdline] r[image.boot.pi-uart] r[image.boot.pi-pcie-gen3]
# The Pi 5 EEPROM reads config.txt from /boot/firmware; the kernel,
# initramfs and DTB are copied alongside it by bes-pi-firmware-update,
# both at build time (below) and on every kernel apt upgrade (via the
# /etc/kernel/postinst.d hook installed further down).
# The Pi 5 EEPROM reads config.txt from /boot/firmware; kernel/initrd/DTB
# are copied alongside it by flash-kernel (pulled in by flash-kernel-piboot
# from packages.sh), both at build time (final dracut + flash-kernel run
# below) and on every kernel apt upgrade via /etc/kernel/postinst.d/zz-flash-kernel.
# serial0,115200 is the Pi 5 PL011 UART (mapped via enable_uart=1 in
# config.txt). It comes last so systemd's serial-getty starts there for
# login, while kernel boot messages still mirror to tty1 (HDMI) for the
# rare case a screen is attached.
#
# config.txt is written before flash-kernel-piboot's postinst runs (the
# package's flash-kernel invocation rewrites this file in-place to add
# os_prefix= keys for the A/B layout — the file must exist for the
# awk-based migrator to succeed).
mkdir -p /boot/firmware
install -m 644 /tmp/files/pi/config.txt /boot/firmware/config.txt
cat > /boot/firmware/cmdline.txt << 'EOF'
console=tty1 console=serial0,115200 root=/dev/mapper/root rootflags=subvol=@,compress=zstd:6 rootfstype=btrfs ro noresume rootwait
EOF

# r[image.boot.pi-firmware-update]
# Install the firmware-partition updater plus its kernel-postinst hook.
# The updater is invoked in two places: explicitly after dracut (below),
# and on every kernel apt upgrade via the postinst hook.
install -m 755 /tmp/files/pi/bes-pi-firmware-update /usr/local/sbin/bes-pi-firmware-update
install -m 755 /tmp/files/pi/zz-bes-pi-firmware /etc/kernel/postinst.d/zz-bes-pi-firmware
# r[image.boot.pi-firmware-update] r[image.boot.pi-tryboot-rollback]
# flash-kernel-piboot pulls in piboot-try, which provides flash-kernel
# (Method: pi-try for the Pi 5B) and the piboot-try-reboot /
# piboot-try-validate services that switch the bootloader between
# current/ and new/ on success / failure of a trial boot. Its postinst
# runs flash-kernel, which rewrites the config.txt installed just above
# to inject `os_prefix=current/` (and `os_prefix=new/` under [tryboot]).
# Boot assets land in /boot/firmware/new/; the explicit flash-kernel run
# at the end of this script (after `dracut --force`) refreshes them with
# the final initramfs.
apt-get install -y -q --no-install-recommends flash-kernel-piboot

# r[image.boot.pi-power-key]
# Pin the power-button behaviour to a clean shutdown. Default systemd
Expand Down Expand Up @@ -477,11 +487,13 @@ dracut --force --kver "$KVER"

if [ "$VARIANT" = "pi" ]; then
# r[image.boot.pi-firmware-update]
# Populate /boot/firmware with the kernel, initramfs and DTB freshly
# produced by dracut. Future kernel apt upgrades trigger the same script
# via /etc/kernel/postinst.d/zz-bes-pi-firmware.
echo "Populating /boot/firmware for Pi 5..."
/usr/local/sbin/bes-pi-firmware-update "$KVER"
# Refresh /boot/firmware/new/ with the freshly-dracut'd initramfs.
# flash-kernel-piboot's postinst ran flash-kernel earlier (when only
# the initramfs-tools-generated initramfs existed); this final run
# supersedes that with the dracut output. Future kernel apt upgrades
# re-trigger flash-kernel via /etc/kernel/postinst.d/zz-flash-kernel.
echo "Refreshing /boot/firmware/new with dracut initramfs..."
flash-kernel
else
# r[image.boot.grub-install] r[image.boot.grub-uuids]
echo "Installing GRUB (target=$GRUB_TARGET)..."
Expand Down
68 changes: 0 additions & 68 deletions image/files/pi/bes-pi-firmware-update

This file was deleted.

13 changes: 8 additions & 5 deletions image/files/pi/config.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# BES Linux — Raspberry Pi 5 boot config.
# Read by the Pi 5 firmware (in EEPROM) at boot. flash-kernel populates the
# kernel image, initramfs, DTB and overlays into this same FAT partition
# (/boot/firmware) on every kernel update.
# Read by the Pi 5 firmware (in EEPROM) at boot. flash-kernel populates
# the kernel image, initramfs, DTB and overlays into /boot/firmware on
# every kernel update.
#
# flash-kernel's first run rewrites this file to inject `os_prefix=current/`
# under `[all]` and `os_prefix=new/` under `[tryboot]` (for the A/B
# rollback layout — see r[image.boot.pi-tryboot-rollback]). It preserves
# everything below, so add new settings here.

# r[image.boot.pi-firmware] r[image.boot.pi-uart] r[image.boot.pi-peripherals]
# r[image.boot.pi-tpm-overlay] r[image.boot.pi-pcie-gen3]
Expand All @@ -27,5 +32,3 @@ dtparam=i2c_arm=on
dtparam=spi=on
dtparam=pciex1_gen=3
dtoverlay=tpm-slb9670
kernel=vmlinuz
initramfs initrd.img followkernel
17 changes: 0 additions & 17 deletions image/files/pi/zz-bes-pi-firmware

This file was deleted.

6 changes: 4 additions & 2 deletions image/packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ case "${VARIANT:-}" in
# peripheral work over the GPIO header. tpm2-tools comes from the
# common list above; Pi 5 has no native TPM but we ship-with-overlay
# for an optional SPI TPM HAT (see r[image.boot.pi-tpm-overlay]).
# Note: no flash-kernel — its noble package lacks a Pi 5 db entry, so
# we manage /boot/firmware ourselves via bes-pi-firmware-update.
# flash-kernel-piboot is installed separately in configure.sh after
# config.txt is written — its postinst runs flash-kernel which needs
# to read the existing config.txt to migrate it to the A/B layout
# (r[image.boot.pi-tryboot-rollback]).
PACKAGES+=(
linux-raspi
linux-firmware-raspi
Expand Down
Loading
Loading