Files
forgefirm/meta-forgefirm/recipes-forgefirm/slotmigrate/files/slotmigrate
T
ScottW514 58d4d91948 Build and release engineering: teardown order, slot safety, release gates
- Controllers stop at K80, before forgectrl at K90: runlevel 0/6 no
  longer tears down the cooling engine, fire gates, and broker while a
  controller may still be executing a job.
- The grblhal/gfcloud init scripts are real emergency levers: stop
  routes through the supervisor (POST /controller/stop - a bare pkill
  was safed and respawned seconds later), start resumes supervision,
  status exists, and the pkill fallback matches full executable paths
  instead of truncated names or bare substrings.
- slotmigrate: the partition grow gets the same 2048-sector tolerance
  as the filesystem branch (an exact compare rewrote the MBR at S02 on
  every boot on disks where the grow cannot land on the last sector),
  verifies it made progress, and the resize2fs retry is bounded at
  three attempts with the counter kept on p3 itself.
- Installer: archive product/platform are verified after the signature,
  and a validly signed OLDER release now requires an explicit yes
  instead of installing as a silent downgrade. All predictable /tmp
  paths in the installer and ffboot are mktemp now.
- release.sh rejects multiple positional versions (the last one used to
  win silently) and a release without factory-era verification dies
  unless explicitly bypassed; mkfw.sh refuses to pack when the public
  key for the post-sign self-check is missing.
- forgefirm-logrotate: size-capped rotation (boot + hourly) for the
  /data logs - a full /data breaks settings, update staging, and the
  controllers own writes.
- Bench build scripts derive every path from their own location or
  FF_SRC_TOP/FF_BUILD_TOP and log to mktemp files.
2026-08-14 18:23:13 -04:00

136 lines
4.6 KiB
Bash

#!/bin/sh
# (C) Copyright 2020-2026
# Scott Wiederhold, s.e.wiederhold@gmail.com
# https://community.openglow.org
# SPDX-License-Identifier: MIT
#
# Legacy-layout migration: reclaims the legacy ForgeFIRM partition (p4)
# and grows /data (p3) to the end of the eMMC, restoring the factory
# disk footprint. Runs in rcS before mountall, so /data is not yet
# mounted. Every step is keyed off the actual disk state - an
# interrupted run resumes on the next boot; a factory-layout disk is a
# fast no-op. Only acts when booted from an eMMC rootfs slot: SD boots
# (bench/dev) never touch the eMMC and p4 itself must not saw off its
# own branch.
DISK=/dev/mmcblk2
P3=${DISK}p3
log () {
echo "slotmigrate: $*"
echo "slotmigrate: $*" > /dev/kmsg 2>/dev/null
}
case "$1" in
start|"") ;;
*) exit 0 ;;
esac
# Runs at S02, possibly before the sysfs/proc mount scripts.
mountpoint -q /proc 2>/dev/null || mount -t proc proc /proc 2>/dev/null
mountpoint -q /sys 2>/dev/null || mount -t sysfs sysfs /sys 2>/dev/null
[ -r /proc/cmdline ] || exit 0
ROOT=$(sed -n 's/.*root=\([^ ]*\).*/\1/p' /proc/cmdline)
case "$ROOT" in
/dev/mmcblk2p1|/dev/mmcblk2p2) ;;
*) exit 0 ;;
esac
command -v sfdisk >/dev/null || { log "sfdisk missing, skipping"; exit 0; }
if grep -q "^${P3} " /proc/mounts; then
log "/data already mounted, skipping this boot"
exit 0
fi
# --- partition table -----------------------------------------------------
if sfdisk -d "$DISK" 2>/dev/null | grep -q "^${DISK}p4"; then
log "removing legacy partition p4"
sfdisk --no-reread --force --delete "$DISK" 4 >/dev/null 2>&1 \
|| { log "p4 delete FAILED"; exit 0; }
partx -d --nr 4 "$DISK" 2>/dev/null
fi
DISK_SECT=$(cat /sys/class/block/mmcblk2/size 2>/dev/null)
P3_START=$(sfdisk -d "$DISK" 2>/dev/null | sed -n "s|^${P3} : start=[ ]*\([0-9]*\),.*|\1|p")
P3_SIZE=$(sfdisk -d "$DISK" 2>/dev/null | sed -n "s|^${P3} .*size=[ ]*\([0-9]*\),.*|\1|p")
[ -n "$DISK_SECT" ] && [ -n "$P3_START" ] && [ -n "$P3_SIZE" ] \
|| { log "cannot read disk/p3 geometry"; exit 0; }
# 2048-sector tolerance (mirrors the filesystem branch): on a disk where
# the grow cannot land exactly on the last sector, an exact comparison
# would rewrite the MBR at S02 on EVERY boot - and a power loss inside
# that window costs the partition table and /data.
if [ $((P3_START + P3_SIZE)) -lt $((DISK_SECT - 2048)) ]; then
log "growing p3 toward the end of the disk ($((P3_START + P3_SIZE)) -> $DISK_SECT sectors)"
echo ", +" | sfdisk --no-reread --force -N 3 "$DISK" >/dev/null 2>&1 \
|| { log "p3 grow FAILED"; exit 0; }
partx -u --nr 3 "$DISK" 2>/dev/null
P3_NEW=$(sfdisk -d "$DISK" 2>/dev/null | sed -n "s|^${P3} .*size=[ ]*\([0-9]*\),.*|\1|p")
if [ -n "$P3_NEW" ] && [ "$P3_NEW" = "$P3_SIZE" ]; then
log "p3 grow made no progress ($P3_SIZE sectors); leaving the table alone"
exit 0
fi
fi
# --- filesystem ----------------------------------------------------------
PART_SECT=$(cat /sys/class/block/mmcblk2p3/size 2>/dev/null)
[ -n "$PART_SECT" ] || { log "cannot read p3 size"; exit 0; }
FS_BLOCKS=$(tune2fs -l "$P3" 2>/dev/null | sed -n 's/^Block count:[ ]*//p')
FS_BSIZE=$(tune2fs -l "$P3" 2>/dev/null | sed -n 's/^Block size:[ ]*//p')
[ -n "$FS_BLOCKS" ] && [ -n "$FS_BSIZE" ] || { log "cannot read p3 filesystem"; exit 0; }
# Bounded retry: a resize that keeps failing must not cost a full
# e2fsck pass on every boot forever. Nothing else is mounted this
# early, so the attempt counter lives on p3 itself.
TRY_FILE=.slotmigrate-resize-tries
read_tries () {
TRIES=0
T=$(mktemp -d) || return
if mount "$P3" "$T" 2>/dev/null; then
TRIES=$(cat "$T/$TRY_FILE" 2>/dev/null)
umount "$T" 2>/dev/null
fi
rmdir "$T" 2>/dev/null
case "$TRIES" in
''|*[!0-9]*) TRIES=0 ;;
esac
}
write_tries () {
T=$(mktemp -d) || return
if mount "$P3" "$T" 2>/dev/null; then
if [ "$1" -gt 0 ]; then
echo "$1" > "$T/$TRY_FILE"
else
rm -f "$T/$TRY_FILE"
fi
umount "$T" 2>/dev/null
fi
rmdir "$T" 2>/dev/null
}
FS_SECT=$((FS_BLOCKS * (FS_BSIZE / 512)))
if [ "$FS_SECT" -lt $((PART_SECT - 2048)) ]; then
read_tries
if [ "$TRIES" -ge 3 ]; then
log "resize2fs failed $TRIES times; giving up (grow /data manually with resize2fs $P3)"
exit 0
fi
log "growing /data filesystem ($FS_SECT -> $PART_SECT sectors)"
e2fsck -f -p "$P3" >/dev/null 2>&1
RC=$?
if [ "$RC" -ge 4 ]; then
log "e2fsck found errors (rc=$RC), NOT resizing"
exit 0
fi
if resize2fs "$P3" >/dev/null 2>&1; then
log "/data grown to full size"
write_tries 0
else
log "resize2fs FAILED (attempt $((TRIES + 1)) of 3)"
write_tries $((TRIES + 1))
fi
fi
exit 0