#!/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 <