cuckoo-escapement/kernel/release/install-rt-kernel.sh
Ryan Malloy 3b505644b2 Kernel release 6.18.38-rt-cuckoo+: reproducible build, guarded installer
Rebuilt with KBUILD_BUILD_USER/HOST/TIMESTAMP pinned. Without them the kernel
bakes the builder's user@hostname and wall-clock build time into /proc/version,
which publishes operator identity in a public artifact and makes the build
unreproducible. Pinned, anyone can rebuild and diff the bytes — the only trust
story available for an unsigned community kernel.

The installer refuses to run on anything but a Pi 4, refuses a mismatched
image/modules pair (vermagic failure means no /dev/pps0 on a kernel that
otherwise boots fine — silent and miserable to debug), verifies SHA256SUMS,
never touches kernel8.img, and leaves rollback as deleting one config.txt line.

Boot-tested on the Pi: Stratum 1, 273 ns offset, PPS handler confirmed NOT
threaded. Binaries ship as release assets, not in git.
2026-07-14 09:52:17 -06:00

86 lines
3.4 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Install the Cuckoo Escapement PREEMPT_RT kernel on a Raspberry Pi 4.
#
# The design goal here is ONE THING: you must never need physical access to the
# SD card because of something this script did. So:
#
# - kernel8.img (the stock kernel) is never touched
# - the RT kernel installs under a NEW filename
# - the ONLY change to config.txt is a single appended line
#
# If it doesn't boot: pull the card, open config.txt on any machine (it's a FAT
# partition, so a Windows laptop will do), delete that one line, put it back.
# The stock kernel returns. That's the entire rollback.
#
# Run it FROM the directory holding the release files.
set -euo pipefail
IMG_NAME="kernel-rt.img"
BOOT="/boot/firmware"
die() { printf '\nERROR: %s\n' "$1" >&2; exit 1; }
[[ $EUID -eq 0 ]] || die "run with sudo"
# --- Refuse to run on hardware this kernel was not built for. A bcm2711 kernel
# --- on a Pi 5 does not boot, and the failure mode is a black screen on a
# --- headless box, which is exactly what we promised wouldn't happen.
MODEL="$(tr -d '\0' < /proc/device-tree/model 2>/dev/null || echo unknown)"
case "$MODEL" in
*"Raspberry Pi 4"*|*"Raspberry Pi Compute Module 4"*|*"Raspberry Pi 400"*) ;;
*) die "this kernel is bcm2711 (Pi 4) only — found: $MODEL" ;;
esac
[[ "$(uname -m)" == "aarch64" ]] || die "need a 64-bit OS (uname -m says $(uname -m))"
[[ -d "$BOOT" ]] || die "$BOOT not found — is this Raspberry Pi OS?"
IMG_GZ="$(ls -1 kernel-rt-*.img.gz 2>/dev/null | head -1)" || true
MODS="$(ls -1 rt-modules-*.tar.gz 2>/dev/null | head -1)" || true
[[ -n "${IMG_GZ:-}" && -n "${MODS:-}" ]] || die "run this from the directory containing the release files"
# --- The image and the modules MUST come from the same build. A mismatched pair
# --- means modprobe fails on vermagic — and since pps-gpio is a module, that
# --- means no /dev/pps0 and no refclock, on a kernel that otherwise boots fine.
# --- Silent, and miserable to debug. Check it up front.
IMG_VER="${IMG_GZ#kernel-rt-}"; IMG_VER="${IMG_VER%.img.gz}"
MOD_VER="${MODS#rt-modules-}"; MOD_VER="${MOD_VER%.tar.gz}"
[[ "$IMG_VER" == "$MOD_VER" ]] \
|| die "image ($IMG_VER) and modules ($MOD_VER) are from different builds"
if [[ -f SHA256SUMS ]]; then
echo "==> verifying checksums"
sha256sum -c SHA256SUMS || die "checksum mismatch — do not install this"
else
echo "!! no SHA256SUMS present — skipping verification (not recommended)"
fi
echo "==> installing modules for $MOD_VER"
tar -C / -xzf "$MODS"
depmod -a "$MOD_VER"
echo "==> installing kernel as $BOOT/$IMG_NAME (kernel8.img left alone)"
zcat "$IMG_GZ" > "$BOOT/$IMG_NAME"
if grep -qE "^kernel=$IMG_NAME" "$BOOT/config.txt"; then
echo "==> config.txt already selects $IMG_NAME"
else
cp "$BOOT/config.txt" "$BOOT/config.txt.before-rt"
printf '\n# Cuckoo Escapement RT kernel. DELETE THIS LINE to boot the stock kernel.\nkernel=%s\n' \
"$IMG_NAME" >> "$BOOT/config.txt"
echo "==> appended 'kernel=$IMG_NAME' to config.txt (backup: config.txt.before-rt)"
fi
cat <<EOF
Done. Reboot, then confirm:
uname -a # expect $MOD_VER ... SMP PREEMPT_RT
ps -eo comm | grep irq/.*pps # expect NOTHING — the handler must not be
# a thread. If you see one, the patch isn't
# in this kernel and your jitter will be bad.
To roll back: delete the 'kernel=$IMG_NAME' line from $BOOT/config.txt
(or restore config.txt.before-rt) and reboot.
EOF