fix: make RPi partition 1 self-sufficient boot fallback
Some checks failed
CI / Go Tests (push) Has been cancelled
CI / Build Go Binaries (amd64, linux, linux-amd64) (push) Has been cancelled
CI / Build Go Binaries (arm64, linux, linux-arm64) (push) Has been cancelled
CI / Shellcheck (push) Has been cancelled
Release / Test (push) Has been cancelled
Release / Build Binaries (amd64, linux, linux-amd64) (push) Has been cancelled
Release / Build Binaries (arm64, linux, linux-arm64) (push) Has been cancelled
Release / Build ISO (amd64) (push) Has been cancelled
Release / Create Release (push) Has been cancelled

The autoboot.txt A/B redirect requires newer RPi EEPROM firmware.
On older EEPROMs, autoboot.txt is silently ignored and the firmware
tries to boot from partition 1 directly — failing with a rainbow
screen because partition 1 had no kernel or initramfs.

Changes:
- Increase partition 1 from 32 MB to 384 MB
- Populate partition 1 with full boot files (kernel, initramfs,
  config.txt with kernel= directive, DTBs, overlays)
- Keep autoboot.txt for A/B redirect on supported EEPROMs
- When autoboot.txt works: boots from partition 2 (A/B scheme)
- When autoboot.txt is unsupported: boots from partition 1 (fallback)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 18:52:21 -06:00
parent 2ba816bf6e
commit a2764218fc

View File

@@ -2,14 +2,14 @@
# create-rpi-image.sh — Create a raw disk image for Raspberry Pi SD card # create-rpi-image.sh — Create a raw disk image for Raspberry Pi SD card
# #
# Partition layout (MBR): # Partition layout (MBR):
# Part 1: Boot Control (32 MB, FAT32, label KSOLOCTL) — firmware + autoboot.txt # Part 1: Boot/Control (384 MB, FAT32, label KSOLOCTL) — firmware + kernel + initramfs + autoboot.txt
# Part 2: Boot A (256 MB, FAT32, label KSOLOA) — kernel + DTBs + initramfs # Part 2: Boot A (256 MB, FAT32, label KSOLOA) — kernel + DTBs + initramfs
# Part 3: Boot B (256 MB, FAT32, label KSOLOB) — same as Boot A (initially identical) # Part 3: Boot B (256 MB, FAT32, label KSOLOB) — same as Boot A (initially identical)
# Part 4: Data (remaining of 2GB, ext4, label KSOLODATA) # Part 4: Data (remaining of 2GB, ext4, label KSOLODATA)
# #
# The RPi EEPROM loads start4.elf from partition 1 (KSOLOCTL). # The RPi EEPROM loads start4.elf from partition 1.
# autoboot.txt on partition 1 redirects boot_partition to 2 (Boot A) or 3 (Boot B). # If autoboot.txt is supported (newer EEPROM), firmware redirects to partition 2/3 for A/B boot.
# The firmware then loads config.txt, kernel, and initramfs from the selected partition. # If autoboot.txt is NOT supported (older EEPROM), partition 1 has full boot files as fallback.
# #
# MBR is required — GPT + autoboot.txt is not reliably supported on Pi 4. # MBR is required — GPT + autoboot.txt is not reliably supported on Pi 4.
# #
@@ -69,15 +69,16 @@ dd if=/dev/zero of="$IMG_OUTPUT" bs=1M count=0 seek="$IMG_SIZE_MB" 2>/dev/null
# --- Partition table (MBR) --- # --- Partition table (MBR) ---
# MBR is required for reliable RPi boot with autoboot.txt. # MBR is required for reliable RPi boot with autoboot.txt.
# GPT + autoboot.txt fails on many Pi 4 EEPROM versions. # GPT + autoboot.txt fails on many Pi 4 EEPROM versions.
# Part 1: Boot Control 32 MB FAT32 (firmware + autoboot.txt) # Part 1: Boot/Control 384 MB FAT32 (firmware + kernel + initramfs + autoboot.txt)
# Part 2: Boot A 256 MB FAT32 (kernel + initramfs + DTBs) # Part 2: Boot A 256 MB FAT32 (kernel + initramfs + DTBs)
# Part 3: Boot B 256 MB FAT32 (kernel + initramfs + DTBs) # Part 3: Boot B 256 MB FAT32 (kernel + initramfs + DTBs)
# Part 4: Data remaining ext4 # Part 4: Data remaining ext4
sfdisk "$IMG_OUTPUT" << EOF sfdisk "$IMG_OUTPUT" << EOF
label: dos label: dos
# Boot Control partition: 32 MB, FAT32 (type 0c = W95 FAT32 LBA) # Boot/Control partition: 384 MB, FAT32 (type 0c = W95 FAT32 LBA)
start=2048, size=65536, type=c, bootable # Contains firmware + autoboot.txt for A/B redirect, PLUS full boot files as fallback
start=2048, size=786432, type=c, bootable
# Boot A partition: 256 MB, FAT32 # Boot A partition: 256 MB, FAT32
size=524288, type=c size=524288, type=c
# Boot B partition: 256 MB, FAT32 # Boot B partition: 256 MB, FAT32
@@ -149,46 +150,6 @@ mount "$P2" "$MNT_BOOTA"
mount "$P3" "$MNT_BOOTB" mount "$P3" "$MNT_BOOTB"
mount "$P4" "$MNT_DATA" mount "$P4" "$MNT_DATA"
# --- Boot Control Partition (KSOLOCTL) ---
# The RPi EEPROM loads start4.elf from partition 1.
# autoboot.txt tells the firmware which partition has config.txt + kernel.
echo " Writing autoboot.txt + firmware to boot control partition..."
cat > "$MNT_CTL/autoboot.txt" << 'AUTOBOOT'
[all]
tryboot_a_b=1
boot_partition=2
[tryboot]
boot_partition=3
AUTOBOOT
# Minimal config.txt on partition 1 — firmware reads this BEFORE autoboot.txt
# to determine arm_64bit mode and GPU settings. Without arm_64bit=1 here,
# the firmware defaults to 32-bit and can't load our 64-bit kernel.
cat > "$MNT_CTL/config.txt" << 'CFGTXT'
arm_64bit=1
enable_uart=1
gpu_mem=16
CFGTXT
# Copy firmware blobs — REQUIRED on partition 1 for EEPROM to boot
if ls "$RPI_FIRMWARE_DIR"/start*.elf 1>/dev/null 2>&1; then
cp "$RPI_FIRMWARE_DIR"/start*.elf "$MNT_CTL/"
fi
if ls "$RPI_FIRMWARE_DIR"/fixup*.dat 1>/dev/null 2>&1; then
cp "$RPI_FIRMWARE_DIR"/fixup*.dat "$MNT_CTL/"
fi
if [ -f "$RPI_FIRMWARE_DIR/bootcode.bin" ]; then
cp "$RPI_FIRMWARE_DIR/bootcode.bin" "$MNT_CTL/"
fi
# Copy DTBs to partition 1 — firmware may need them before redirecting
if ls "$RPI_FIRMWARE_DIR"/bcm27*.dtb 1>/dev/null 2>&1; then
cp "$RPI_FIRMWARE_DIR"/bcm27*.dtb "$MNT_CTL/"
fi
if [ -d "$RPI_FIRMWARE_DIR/overlays" ]; then
cp -r "$RPI_FIRMWARE_DIR/overlays" "$MNT_CTL/"
fi
# --- Helper: populate a boot partition --- # --- Helper: populate a boot partition ---
populate_boot_partition() { populate_boot_partition() {
local MNT="$1" local MNT="$1"
@@ -231,6 +192,39 @@ CFGTXT
echo "$VERSION" > "$MNT/version.txt" echo "$VERSION" > "$MNT/version.txt"
} }
# --- Boot Control Partition (KSOLOCTL) ---
# Partition 1 serves dual purpose:
# 1. Contains firmware + autoboot.txt for A/B redirect (if EEPROM supports it)
# 2. Contains full boot files (kernel + initramfs) as fallback if autoboot.txt isn't supported
echo " Writing firmware + autoboot.txt + boot files to partition 1..."
# autoboot.txt — tells firmware which partition to boot from (A/B switching)
# If the EEPROM doesn't support this, it's silently ignored and the firmware
# falls back to booting from partition 1 using config.txt below.
cat > "$MNT_CTL/autoboot.txt" << 'AUTOBOOT'
[all]
tryboot_a_b=1
boot_partition=2
[tryboot]
boot_partition=3
AUTOBOOT
# Copy firmware blobs — REQUIRED on partition 1 for EEPROM to boot
if ls "$RPI_FIRMWARE_DIR"/start*.elf 1>/dev/null 2>&1; then
cp "$RPI_FIRMWARE_DIR"/start*.elf "$MNT_CTL/"
fi
if ls "$RPI_FIRMWARE_DIR"/fixup*.dat 1>/dev/null 2>&1; then
cp "$RPI_FIRMWARE_DIR"/fixup*.dat "$MNT_CTL/"
fi
if [ -f "$RPI_FIRMWARE_DIR/bootcode.bin" ]; then
cp "$RPI_FIRMWARE_DIR/bootcode.bin" "$MNT_CTL/"
fi
# Full boot files on partition 1 — fallback if autoboot.txt redirect doesn't work.
# When autoboot.txt works, firmware switches to partition 2 and reads config.txt there.
# When autoboot.txt is unsupported, firmware reads THIS config.txt and boots from here.
populate_boot_partition "$MNT_CTL" "Boot Control (KSOLOCTL)"
# --- Boot A Partition (KSOLOA) --- # --- Boot A Partition (KSOLOA) ---
populate_boot_partition "$MNT_BOOTA" "Boot A (KSOLOA)" populate_boot_partition "$MNT_BOOTA" "Boot A (KSOLOA)"
@@ -248,7 +242,7 @@ sync
echo "" echo ""
echo "==> Raspberry Pi disk image created: $IMG_OUTPUT" echo "==> Raspberry Pi disk image created: $IMG_OUTPUT"
echo " Size: $(du -h "$IMG_OUTPUT" | cut -f1)" echo " Size: $(du -h "$IMG_OUTPUT" | cut -f1)"
echo " Part 1 (KSOLOCTL): Firmware + autoboot.txt (boot control)" echo " Part 1 (KSOLOCTL): Firmware + kernel + initramfs + autoboot.txt (boot/control)"
echo " Part 2 (KSOLOA): Boot A — kernel + initramfs + DTBs" echo " Part 2 (KSOLOA): Boot A — kernel + initramfs + DTBs"
echo " Part 3 (KSOLOB): Boot B — kernel + initramfs + DTBs" echo " Part 3 (KSOLOB): Boot B — kernel + initramfs + DTBs"
echo " Part 4 (KSOLODATA): Persistent K8s state" echo " Part 4 (KSOLODATA): Persistent K8s state"