diff --git a/docs/GUIDE-IMAGES.md b/docs/GUIDE-IMAGES.md index 3774aeac..e665d451 100644 --- a/docs/GUIDE-IMAGES.md +++ b/docs/GUIDE-IMAGES.md @@ -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. diff --git a/docs/spec/disk-images.md b/docs/spec/disk-images.md index d6b5987b..29318ed3 100644 --- a/docs/spec/disk-images.md +++ b/docs/spec/disk-images.md @@ -200,9 +200,9 @@ 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 @@ -210,14 +210,23 @@ For the `pi` variant, kernel command-line arguments are read from 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 diff --git a/image/configure.sh b/image/configure.sh index e6657034..d6618b2b 100755 --- a/image/configure.sh +++ b/image/configure.sh @@ -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 @@ -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)..." diff --git a/image/files/pi/bes-pi-firmware-update b/image/files/pi/bes-pi-firmware-update deleted file mode 100755 index 7fa92be3..00000000 --- a/image/files/pi/bes-pi-firmware-update +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash -# Re-populate /boot/firmware (the Pi 5 boot partition) with the kernel, -# initramfs, DTB and overlays for the given kernel version. Invoked by: -# - configure.sh during image build (after dracut) -# - /etc/kernel/postinst.d/zz-bes-pi-firmware on kernel package upgrades -# -# The Pi 5 EEPROM bootloader reads from this partition and loads vmlinuz + -# initrd.img + the matching DTB at boot. Without this script, kernel apt -# upgrades would update /boot/* but leave the firmware partition stale. -set -euo pipefail - -KVER="${1:-}" -if [ -z "$KVER" ]; then - echo "Usage: $(basename "$0") " >&2 - exit 2 -fi - -FW=/boot/firmware -if [ ! -d "$FW" ]; then - # Not a Pi image (or firmware partition not mounted) — nothing to do. - exit 0 -fi - -# --- Kernel --- -SRC_KERNEL="/boot/vmlinuz-$KVER" -if [ ! -f "$SRC_KERNEL" ]; then - echo "ERROR: $SRC_KERNEL not found" >&2 - exit 1 -fi -install -m 644 "$SRC_KERNEL" "$FW/vmlinuz" - -# --- Initramfs --- -SRC_INITRD="/boot/initrd.img-$KVER" -if [ -f "$SRC_INITRD" ]; then - install -m 644 "$SRC_INITRD" "$FW/initrd.img" -fi - -# --- Device-tree blob and overlays --- -# linux-raspi ships per-kernel DTBs under /usr/lib/firmware//device-tree/. -# A few historical layouts also exist; probe them in priority order. -DTB_NAME="bcm2712-rpi-5-b.dtb" -DTB_DIR="" -for candidate in \ - "/usr/lib/firmware/${KVER}/device-tree/broadcom" \ - "/lib/firmware/${KVER}/device-tree/broadcom" \ - "/usr/lib/linux-image-${KVER}/broadcom"; do - if [ -f "${candidate}/${DTB_NAME}" ]; then - DTB_DIR="$candidate" - break - fi -done - -if [ -z "$DTB_DIR" ]; then - echo "ERROR: could not locate ${DTB_NAME} for kernel ${KVER}" >&2 - echo "Searched:" >&2 - echo " /usr/lib/firmware/${KVER}/device-tree/broadcom" >&2 - echo " /lib/firmware/${KVER}/device-tree/broadcom" >&2 - echo " /usr/lib/linux-image-${KVER}/broadcom" >&2 - exit 1 -fi -install -m 644 "${DTB_DIR}/${DTB_NAME}" "$FW/${DTB_NAME}" - -# Overlays: directory sibling of the DTB -OVERLAYS_SRC="$(dirname "$DTB_DIR")/overlays" -if [ -d "$OVERLAYS_SRC" ]; then - rm -rf "$FW/overlays" - cp -a "$OVERLAYS_SRC" "$FW/overlays" -fi diff --git a/image/files/pi/config.txt b/image/files/pi/config.txt index e8f72314..8bfc738f 100644 --- a/image/files/pi/config.txt +++ b/image/files/pi/config.txt @@ -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] @@ -27,5 +32,3 @@ dtparam=i2c_arm=on dtparam=spi=on dtparam=pciex1_gen=3 dtoverlay=tpm-slb9670 -kernel=vmlinuz -initramfs initrd.img followkernel diff --git a/image/files/pi/zz-bes-pi-firmware b/image/files/pi/zz-bes-pi-firmware deleted file mode 100755 index f658a6a9..00000000 --- a/image/files/pi/zz-bes-pi-firmware +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# Kernel-postinst hook: keep /boot/firmware in sync with newly-installed -# kernels on the pi variant. Receives the kernel version as $1; the package -# manager runs every script in /etc/kernel/postinst.d/ after a kernel install -# or upgrade. -# -# `zz-` prefix orders this after dracut's own /etc/kernel/postinst.d/dracut -# so the initramfs we copy is the freshly-generated one. -set -e - -KVER="${1:-}" -[ -n "$KVER" ] || exit 0 - -# Only run on the pi variant. Other variants don't have /boot/firmware. -[ -d /boot/firmware ] || exit 0 - -exec /usr/local/sbin/bes-pi-firmware-update "$KVER" diff --git a/image/packages.sh b/image/packages.sh index 67b9b89a..cf269282 100644 --- a/image/packages.sh +++ b/image/packages.sh @@ -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 diff --git a/scripts/migrate-pi-to-piboot.sh b/scripts/migrate-pi-to-piboot.sh new file mode 100755 index 00000000..b65b2352 --- /dev/null +++ b/scripts/migrate-pi-to-piboot.sh @@ -0,0 +1,157 @@ +#!/bin/bash +# Migrate a pi image already flashed with the legacy single-directory +# /boot/firmware layout (kernel/initrd/DTB at the root) to the A/B +# tryboot layout under current/ + new/ + old/. See +# r[image.boot.pi-tryboot-rollback] in docs/spec/disk-images.md. +# +# Steps: +# 1. Pre-flight: this is a pi image, /boot/firmware is mounted, the +# legacy hand-rolled helper is present (i.e. needs migration), and +# the EEPROM firmware is recent enough to honour tryboot. +# 2. Install flash-kernel-piboot (pulls in piboot-try). +# 3. Remove the legacy helper + kernel postinst hook. +# 4. Run flash-kernel — its migrate() function copies the existing +# kernel/initrd/DTB/overlays into current/, rewrites config.txt +# with os_prefix= keys, and writes autoboot.txt with tryboot_a_b=1. +# 5. Verify the new layout is in place. +# +# Run as root. Re-runnable: if flash-kernel-piboot is already installed +# and the layout is already A/B, the script reports "nothing to do" and +# exits 0. +set -euo pipefail + +# --- Pre-flight --------------------------------------------------------- + +if [ "$(id -u)" -ne 0 ]; then + echo "ERROR: must run as root" >&2 + exit 1 +fi + +if [ ! -r /etc/bes/image-variant ] || [ "$(cat /etc/bes/image-variant)" != "pi" ]; then + echo "ERROR: not a pi image (check /etc/bes/image-variant)" >&2 + exit 1 +fi + +if ! mountpoint -q /boot/firmware; then + echo "ERROR: /boot/firmware is not mounted" >&2 + exit 1 +fi + +# Detect whether we have anything to do. The pi-try migration is +# already complete when current/state exists OR config.txt already has +# os_prefix= (mirrors needs_migrate() in /usr/share/flash-kernel/functions-piboot). +ALREADY_MIGRATED=0 +if [ -f /boot/firmware/current/state ] || \ + grep -q '^os_prefix=' /boot/firmware/config.txt 2>/dev/null; then + ALREADY_MIGRATED=1 +fi + +HAS_LEGACY_HELPER=0 +if [ -x /usr/local/sbin/bes-pi-firmware-update ] || \ + [ -e /etc/kernel/postinst.d/zz-bes-pi-firmware ]; then + HAS_LEGACY_HELPER=1 +fi + +if [ "$ALREADY_MIGRATED" -eq 1 ] && [ "$HAS_LEGACY_HELPER" -eq 0 ]; then + echo "Already migrated (config.txt has os_prefix= and no legacy helper present). Nothing to do." + exit 0 +fi + +# EEPROM firmware floor: tryboot on Pi 5 / 500 / CM5 needs firmware +# dated 2025-02-11 or later (Ubuntu 26.04 release notes). +EEPROM_FLOOR="2025-02-11" + +if ! command -v vcgencmd >/dev/null 2>&1; then + echo "ERROR: vcgencmd not found — cannot verify EEPROM firmware date" >&2 + echo " apt-get install libraspberrypi-bin" >&2 + exit 1 +fi + +# `vcgencmd bootloader_version` first line is the date — either +# "YYYY-MM-DD HH:MM:SS" or "YYYY/MM/DD HH:MM:SS" depending on the EEPROM +# release. Normalise to dash form so the string comparison below works +# byte-for-byte against EEPROM_FLOOR. +EEPROM_LINE="$(vcgencmd bootloader_version | head -n1 | tr -d '\r')" +EEPROM_DATE="${EEPROM_LINE%% *}" +EEPROM_DATE="${EEPROM_DATE//\//-}" +if [ -z "$EEPROM_DATE" ] || ! [[ "$EEPROM_DATE" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then + echo "ERROR: could not parse EEPROM date from vcgencmd output: $EEPROM_LINE" >&2 + exit 1 +fi + +if [ "$EEPROM_DATE" \< "$EEPROM_FLOOR" ]; then + echo "ERROR: EEPROM firmware date $EEPROM_DATE is older than $EEPROM_FLOOR" >&2 + echo " The tryboot A/B mechanism requires newer EEPROM firmware." >&2 + echo " Update with: sudo rpi-eeprom-update -a && sudo reboot" >&2 + echo " Then re-run this script." >&2 + exit 1 +fi +echo "EEPROM firmware date $EEPROM_DATE meets floor $EEPROM_FLOOR." + +# --- Install flash-kernel-piboot ---------------------------------------- + +if ! dpkg -s flash-kernel-piboot >/dev/null 2>&1; then + echo "Installing flash-kernel-piboot..." + apt-get update -q + # --no-install-recommends matches the image build choice (see + # image/packages.sh + image/configure.sh). + DEBIAN_FRONTEND=noninteractive apt-get install -y -q \ + --no-install-recommends flash-kernel-piboot +else + echo "flash-kernel-piboot already installed." +fi + +# --- Remove the legacy helper ------------------------------------------- + +# Order matters: drop the kernel postinst hook first so a parallel +# upgrade can't fire it after flash-kernel takes over. +if [ -e /etc/kernel/postinst.d/zz-bes-pi-firmware ]; then + echo "Removing /etc/kernel/postinst.d/zz-bes-pi-firmware..." + rm -f /etc/kernel/postinst.d/zz-bes-pi-firmware +fi +if [ -e /usr/local/sbin/bes-pi-firmware-update ]; then + echo "Removing /usr/local/sbin/bes-pi-firmware-update..." + rm -f /usr/local/sbin/bes-pi-firmware-update +fi + +# --- Run flash-kernel --------------------------------------------------- +# +# flash-kernel detects Method: pi-try for the Pi 5B, sees that +# needs_migrate() is true (no current/state, no os_prefix= in +# config.txt), and runs migrate() to convert the layout in-place. +# Subsequent runs become a normal "stage assets into new/" operation. +echo "Running flash-kernel to migrate /boot/firmware layout..." +flash-kernel + +# --- Verify ------------------------------------------------------------- + +FAIL=0 +verify() { + local desc="$1"; shift + if "$@"; then + echo " OK: $desc" + else + echo " FAIL: $desc" >&2 + FAIL=1 + fi +} + +verify "/boot/firmware/current/ exists" test -d /boot/firmware/current +verify "/boot/firmware/current/state is good" sh -c '[ "$(cat /boot/firmware/current/state 2>/dev/null)" = good ]' +verify "/boot/firmware/current/vmlinuz exists" test -f /boot/firmware/current/vmlinuz +verify "/boot/firmware/current/initrd.img exists" test -f /boot/firmware/current/initrd.img +verify "config.txt sets os_prefix=current/" grep -q '^os_prefix=current/' /boot/firmware/config.txt +verify "config.txt sets os_prefix=new/" grep -q '^os_prefix=new/' /boot/firmware/config.txt +verify "autoboot.txt enables tryboot_a_b" grep -q '^tryboot_a_b=1' /boot/firmware/autoboot.txt +verify "legacy bes-pi-firmware-update absent" sh -c '! test -e /usr/local/sbin/bes-pi-firmware-update' +verify "legacy zz-bes-pi-firmware hook absent" sh -c '! test -e /etc/kernel/postinst.d/zz-bes-pi-firmware' + +if [ "$FAIL" -ne 0 ]; then + echo "ERROR: migration verification failed — inspect /boot/firmware" >&2 + exit 1 +fi + +echo "" +echo "Migration complete. Reboot to start booting from /boot/firmware/current/." +echo "Note: the first kernel apt upgrade after this point will stage assets" +echo "into /boot/firmware/new/ and trigger a one-time tryboot on reboot." diff --git a/tests/test-image-structure.sh b/tests/test-image-structure.sh index 33a79a25..c981da98 100755 --- a/tests/test-image-structure.sh +++ b/tests/test-image-structure.sh @@ -379,7 +379,9 @@ if [ "$VARIANT" = "pi" ]; then check "Pi config.txt exists" test -f "$MNT/boot/firmware/config.txt" check "Pi cmdline.txt exists" test -f "$MNT/boot/firmware/cmdline.txt" if [ -f "$MNT/boot/firmware/config.txt" ]; then - check "config.txt selects vmlinuz kernel" grep -q '^kernel=vmlinuz' "$MNT/boot/firmware/config.txt" + # r[verify image.boot.pi-tryboot-rollback] + check "config.txt sets os_prefix=current/" grep -q '^os_prefix=current/' "$MNT/boot/firmware/config.txt" + check "config.txt sets os_prefix=new/ under [tryboot]" grep -q '^os_prefix=new/' "$MNT/boot/firmware/config.txt" # r[verify image.boot.pi-uart] check "config.txt enables UART" grep -q '^enable_uart=1' "$MNT/boot/firmware/config.txt" # r[verify image.boot.pi-peripherals] @@ -397,12 +399,17 @@ if [ "$VARIANT" = "pi" ]; then # r[verify image.boot.pi-uart] check "cmdline.txt sets serial0 console" grep -q 'console=serial0,115200' "$MNT/boot/firmware/cmdline.txt" fi + # r[verify image.boot.pi-tryboot-rollback] + check "autoboot.txt enables tryboot_a_b" grep -q '^tryboot_a_b=1' "$MNT/boot/firmware/autoboot.txt" + check "current/state is good" sh -c "test \"\$(cat '$MNT/boot/firmware/current/state' 2>/dev/null)\" = good" # r[verify image.boot.pi-firmware-update] - check "firmware-update script installed" test -x "$MNT/usr/local/sbin/bes-pi-firmware-update" - check "kernel postinst hook installed" test -x "$MNT/etc/kernel/postinst.d/zz-bes-pi-firmware" - check "/boot/firmware has kernel" test -f "$MNT/boot/firmware/vmlinuz" - check "/boot/firmware has initramfs" test -f "$MNT/boot/firmware/initrd.img" - check "/boot/firmware has Pi 5 DTB" test -f "$MNT/boot/firmware/bcm2712-rpi-5-b.dtb" + check "/boot/firmware/current/ has kernel" test -f "$MNT/boot/firmware/current/vmlinuz" + check "/boot/firmware/current/ has initramfs" test -f "$MNT/boot/firmware/current/initrd.img" + check "/boot/firmware/current/ has Pi 5 DTB" test -f "$MNT/boot/firmware/current/bcm2712-rpi-5-b.dtb" + check "kernel postinst hook installed (zz-flash-kernel)" test -x "$MNT/etc/kernel/postinst.d/zz-flash-kernel" + # Legacy hand-rolled hook + helper must not be present (replaced by flash-kernel). + check_not "no legacy bes-pi-firmware-update helper" test -e "$MNT/usr/local/sbin/bes-pi-firmware-update" + check_not "no legacy zz-bes-pi-firmware hook" test -e "$MNT/etc/kernel/postinst.d/zz-bes-pi-firmware" # No GRUB on pi. check_not "no /boot/grub on pi" test -d "$MNT/boot/grub" check_not "no GRUB EFI binary on pi (BOOTAA64.EFI)" test -f "$MNT/boot/firmware/EFI/BOOT/BOOTAA64.EFI"