fix: RPi image uses MBR and firmware on boot partition
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

- Switch from GPT to MBR (dos) partition table — GPT + autoboot.txt
  fails on many Pi 4 EEPROM versions
- Copy firmware blobs (start*.elf, fixup*.dat) to partition 1 (KSOLOCTL)
  so the EEPROM can find and load them
- Increase boot control partition from 16 MB to 32 MB to fit firmware
- Mark partition 1 as bootable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 18:16:34 -06:00
parent ba4812f637
commit 65dcddb47e

View File

@@ -1,15 +1,17 @@
#!/bin/bash #!/bin/bash
# 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 (GPT): # Partition layout (MBR):
# Part 1: Boot Control (16 MB, FAT32, label KSOLOCTL) — autoboot.txt only # Part 1: Boot Control (32 MB, FAT32, label KSOLOCTL) — firmware + autoboot.txt
# Part 2: Boot A (256 MB, FAT32, label KSOLOA) — firmware + 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 uses autoboot.txt in the control partition to implement A/B boot # The RPi EEPROM loads start4.elf from partition 1 (KSOLOCTL).
# via the tryboot mechanism (tryboot_a_b=1). Normal bootpartition 2 (Boot A), # autoboot.txt on partition 1 redirects boot_partition to 2 (Boot A) or 3 (Boot B).
# tryboot → partition 3 (Boot B). # The firmware then loads config.txt, kernel, and initramfs from the selected partition.
#
# MBR is required — GPT + autoboot.txt is not reliably supported on Pi 4.
# #
# Usage: build/scripts/create-rpi-image.sh # Usage: build/scripts/create-rpi-image.sh
set -euo pipefail set -euo pipefail
@@ -64,22 +66,24 @@ mkdir -p "$OUTPUT_DIR"
# --- Create sparse image --- # --- Create sparse image ---
dd if=/dev/zero of="$IMG_OUTPUT" bs=1M count=0 seek="$IMG_SIZE_MB" 2>/dev/null dd if=/dev/zero of="$IMG_OUTPUT" bs=1M count=0 seek="$IMG_SIZE_MB" 2>/dev/null
# --- Partition table (GPT) --- # --- Partition table (MBR) ---
# Part 1: Boot Control 16 MB FAT32 # MBR is required for reliable RPi boot with autoboot.txt.
# Part 2: Boot A 256 MB FAT32 # GPT + autoboot.txt fails on many Pi 4 EEPROM versions.
# Part 3: Boot B 256 MB FAT32 # Part 1: Boot Control 32 MB FAT32 (firmware + autoboot.txt)
# Part 2: Boot A 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: gpt label: dos
# Boot Control partition: 16 MB # Boot Control partition: 32 MB, FAT32 (type 0c = W95 FAT32 LBA)
start=2048, size=32768, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, name="BootCtl" start=2048, size=65536, type=c, bootable
# Boot A partition: 256 MB # Boot A partition: 256 MB, FAT32
size=524288, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, name="BootA" size=524288, type=c
# Boot B partition: 256 MB # Boot B partition: 256 MB, FAT32
size=524288, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, name="BootB" size=524288, type=c
# Data partition: remaining # Data partition: remaining, Linux
type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, name="Data" type=83
EOF EOF
# --- Set up loop device --- # --- Set up loop device ---
@@ -146,7 +150,9 @@ mount "$P3" "$MNT_BOOTB"
mount "$P4" "$MNT_DATA" mount "$P4" "$MNT_DATA"
# --- Boot Control Partition (KSOLOCTL) --- # --- Boot Control Partition (KSOLOCTL) ---
echo " Writing autoboot.txt..." # 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' cat > "$MNT_CTL/autoboot.txt" << 'AUTOBOOT'
[all] [all]
tryboot_a_b=1 tryboot_a_b=1
@@ -155,6 +161,17 @@ boot_partition=2
boot_partition=3 boot_partition=3
AUTOBOOT 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
# --- Helper: populate a boot partition --- # --- Helper: populate a boot partition ---
populate_boot_partition() { populate_boot_partition() {
local MNT="$1" local MNT="$1"
@@ -183,17 +200,6 @@ CFGTXT
# Copy initramfs # Copy initramfs
cp "$INITRAMFS" "$MNT/kubesolo-os.gz" cp "$INITRAMFS" "$MNT/kubesolo-os.gz"
# Copy firmware blobs (start*.elf, fixup*.dat)
if ls "$RPI_FIRMWARE_DIR"/start*.elf 1>/dev/null 2>&1; then
cp "$RPI_FIRMWARE_DIR"/start*.elf "$MNT/"
fi
if ls "$RPI_FIRMWARE_DIR"/fixup*.dat 1>/dev/null 2>&1; then
cp "$RPI_FIRMWARE_DIR"/fixup*.dat "$MNT/"
fi
if [ -f "$RPI_FIRMWARE_DIR/bootcode.bin" ]; then
cp "$RPI_FIRMWARE_DIR/bootcode.bin" "$MNT/"
fi
# Copy DTB overlays # Copy DTB overlays
if [ -d "$RPI_FIRMWARE_DIR/overlays" ]; then if [ -d "$RPI_FIRMWARE_DIR/overlays" ]; then
cp -r "$RPI_FIRMWARE_DIR/overlays" "$MNT/" cp -r "$RPI_FIRMWARE_DIR/overlays" "$MNT/"
@@ -225,9 +231,9 @@ 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): Boot control (autoboot.txt)" echo " Part 1 (KSOLOCTL): Firmware + autoboot.txt (boot control)"
echo " Part 2 (KSOLOA): Boot A — firmware + kernel + initramfs" echo " Part 2 (KSOLOA): Boot A — kernel + initramfs + DTBs"
echo " Part 3 (KSOLOB): Boot B — firmware + kernel + initramfs" echo " Part 3 (KSOLOB): Boot B — kernel + initramfs + DTBs"
echo " Part 4 (KSOLODATA): Persistent K8s state" echo " Part 4 (KSOLODATA): Persistent K8s state"
echo "" echo ""
echo "Write to SD card with:" echo "Write to SD card with:"