mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-28 01:01:12 -07:00
installer v2: single-stage slot install; slotmigrate legacy reclaim
install-forgefirm.sh is now single-stage and never repartitions: run from factory firmware, it archives every factory slot version plus the recovery boot partitions to /data/forgefirm/archive (manifest with md5s), verifies the signed forgefirm.fw against the embedded ForgeFIRM pubkey (raw 32-byte form for the factory's fwup 0.14.2; dev key until the production key ceremony), applies it to the INACTIVE slot with the factory's own fwup, post-verifies the written rootfs, installs /data/ffboot, and flips the saved env with read-back verification. The booted factory slot stays installed and bootable; /data is untouched beyond the archive. Fixed release asset name forgefirm.fw (version in the fwup metadata and release tag). slotmigrate (new recipe, rcS before mountall) reclaims the legacy layout on eMMC-slot boots: deletes p4, grows p3 to the end of the disk (sfdisk + partx BLKPG - works with a sibling partition as root), then e2fsck+resize2fs. Every step is keyed off the actual disk state, so interrupted runs resume and factory-layout disks are a no-op; SD boots never touch the eMMC.
This commit is contained in:
+17
-13
@@ -101,16 +101,20 @@ factory firmware:
|
|||||||
|
|
||||||
1. Sanity: factory 3-partition layout, both slots 200 MiB, active slot
|
1. Sanity: factory 3-partition layout, both slots 200 MiB, active slot
|
||||||
detected (`rdev`), enough `/data` space.
|
detected (`rdev`), enough `/data` space.
|
||||||
2. Archive: identify the **newer** factory version of the two slots;
|
2. Archive: **every factory slot version** not already archived —
|
||||||
`dd | gzip` it to `/data/forgefirm/archive/factory-rootfs-<ver>.img.gz`
|
`dd | gzip` to `/data/forgefirm/archive/factory-rootfs-<ver>.img.gz`
|
||||||
with a manifest (versions of both slots, date); also dump
|
with a manifest line (slot, version, date, md5); also dump
|
||||||
boot0/boot1 (32 MiB) into the archive now, ahead of Phase 5.
|
boot0/boot1 (32 MiB) into the archive now, ahead of Phase 5. With
|
||||||
3. Fetch `forgefirm-<ver>.fw` from GitHub releases (or take a local
|
both slots archived, any later overwrite needs no second archive
|
||||||
file argument for offline/dev installs).
|
step.
|
||||||
4. Apply to the slot holding the **older** factory version (fwup +
|
3. Fetch `forgefirm.fw` from GitHub releases (fixed asset name — the
|
||||||
our pubkey). The newer factory install stays bootable in the other
|
`releases/latest/download/` URL needs one; the version lives in the
|
||||||
slot *and* is archived — so the first ForgeFIRM self-upgrade may
|
fwup metadata and the release tag), or take a local file argument
|
||||||
overwrite it without a second archive step.
|
for offline/dev installs. Verify the signature against the
|
||||||
|
ForgeFIRM pubkey embedded in the installer (raw 32-byte form for
|
||||||
|
the factory's fwup; a dev key until the production ceremony).
|
||||||
|
4. Apply to the **inactive** slot (fwup + our pubkey). The booted
|
||||||
|
factory install stays bootable in the other slot.
|
||||||
5. Atomic env flip (embed the flip logic — the factory rootfs has no
|
5. Atomic env flip (embed the flip logic — the factory rootfs has no
|
||||||
ffboot v2), reboot.
|
ffboot v2), reboot.
|
||||||
|
|
||||||
@@ -202,10 +206,10 @@ selector, factory restore and return — without touching a shell.*
|
|||||||
## Contracts
|
## Contracts
|
||||||
|
|
||||||
- **Artifacts** (consumers: installer, GUI updater, recovery):
|
- **Artifacts** (consumers: installer, GUI updater, recovery):
|
||||||
`forgefirm-<semver>.fw` (signed; tasks `upgrade.a`/`upgrade.b`,
|
`forgefirm.fw` (fixed asset name; signed; version in the fwup
|
||||||
|
metadata = release tag `v<semver>`; tasks `upgrade.a`/`upgrade.b`,
|
||||||
`complete` from Phase 5), `sha256sums.txt`,
|
`complete` from Phase 5), `sha256sums.txt`,
|
||||||
`forgefirm-image-glowforge.rootfs.wic.gz` (SD burns), release tag
|
`forgefirm-image-glowforge.rootfs.wic.gz` (SD burns).
|
||||||
`v<semver>`.
|
|
||||||
- **Env**: SD = `0/0/1//dev/mmcblk1p1`; slot N = `1/0/N//dev/mmcblk2pN`
|
- **Env**: SD = `0/0/1//dev/mmcblk1p1`; slot N = `1/0/N//dev/mmcblk2pN`
|
||||||
(`mmcdev/mmchwpart/mmcpart/mmcroot`, always one transaction).
|
(`mmcdev/mmchwpart/mmcpart/mmcroot`, always one transaction).
|
||||||
- **Archive layout**: `/data/forgefirm/archive/` —
|
- **Archive layout**: `/data/forgefirm/archive/` —
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ IMAGE_INSTALL:remove = "gfui-client"
|
|||||||
# fwup: applies signed .fw archives (ForgeFIRM upgrades + factory restore)
|
# fwup: applies signed .fw archives (ForgeFIRM upgrades + factory restore)
|
||||||
# to the inactive rootfs slot.
|
# to the inactive rootfs slot.
|
||||||
# ffboot: boot-slot inventory and switching (also ships fw_env.config).
|
# ffboot: boot-slot inventory and switching (also ships fw_env.config).
|
||||||
IMAGE_INSTALL:append = " grblhal-glowforge forgectrl gfhome v4l-utils fwup ffboot"
|
# slotmigrate: boot-time reclaim of the legacy p4 layout (grows /data).
|
||||||
|
IMAGE_INSTALL:append = " grblhal-glowforge forgectrl gfhome v4l-utils fwup ffboot slotmigrate"
|
||||||
|
|
||||||
# The release rootfs must fit a 200 MiB factory eMMC slot (409600 blocks).
|
# The release rootfs must fit a 200 MiB factory eMMC slot (409600 blocks).
|
||||||
# Sizing: content + 40 MiB working space, hard-capped at the slot size —
|
# Sizing: content + 40 MiB working space, hard-capped at the slot size —
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
#!/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: $*"; }
|
||||||
|
|
||||||
|
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=$(blockdev --getsz "$DISK")
|
||||||
|
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 "$P3_START" ] && [ -n "$P3_SIZE" ] || { log "cannot read p3 geometry"; exit 0; }
|
||||||
|
|
||||||
|
if [ $((P3_START + P3_SIZE)) -lt "$DISK_SECT" ]; then
|
||||||
|
log "growing p3 to 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
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- filesystem ----------------------------------------------------------
|
||||||
|
PART_SECT=$(blockdev --getsz "$P3")
|
||||||
|
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; }
|
||||||
|
|
||||||
|
FS_SECT=$((FS_BLOCKS * (FS_BSIZE / 512)))
|
||||||
|
if [ "$FS_SECT" -lt $((PART_SECT - 2048)) ]; then
|
||||||
|
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
|
||||||
|
resize2fs "$P3" >/dev/null 2>&1 \
|
||||||
|
&& log "/data grown to full size" \
|
||||||
|
|| log "resize2fs FAILED (will retry next boot)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
SUMMARY = "Boot-time migration from the legacy ForgeFIRM disk layout"
|
||||||
|
DESCRIPTION = "Reclaims the legacy ForgeFIRM eMMC partition (p4) and grows \
|
||||||
|
/data back to the factory footprint. Runs before mountall; state-derived \
|
||||||
|
and idempotent; a factory-layout disk is a no-op. Acts only when booted \
|
||||||
|
from an eMMC rootfs slot."
|
||||||
|
LICENSE = "MIT"
|
||||||
|
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
|
||||||
|
|
||||||
|
SRC_URI = "file://slotmigrate"
|
||||||
|
|
||||||
|
S = "${WORKDIR}"
|
||||||
|
|
||||||
|
inherit update-rc.d
|
||||||
|
|
||||||
|
INITSCRIPT_NAME = "slotmigrate"
|
||||||
|
INITSCRIPT_PARAMS = "start 2 S ."
|
||||||
|
|
||||||
|
RDEPENDS:${PN} = " \
|
||||||
|
util-linux-sfdisk \
|
||||||
|
util-linux-partx \
|
||||||
|
e2fsprogs-e2fsck \
|
||||||
|
e2fsprogs-tune2fs \
|
||||||
|
e2fsprogs-resize2fs \
|
||||||
|
"
|
||||||
|
|
||||||
|
do_install() {
|
||||||
|
install -Dm 0755 ${WORKDIR}/slotmigrate ${D}${sysconfdir}/init.d/slotmigrate
|
||||||
|
}
|
||||||
+164
-119
@@ -4,36 +4,92 @@
|
|||||||
# https://community.openglow.org
|
# https://community.openglow.org
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
#
|
#
|
||||||
# This program makes a partition on the built-in eMMC flash storage on
|
# Single-stage OpenGlow/ForgeFIRM installer. Runs on FACTORY firmware:
|
||||||
# the factory Glowforge, and installs the latest copy of OpenGlow/ForgeFIRM.
|
# 1. archives the factory rootfs slots and the recovery boot partitions
|
||||||
|
# to /data/forgefirm/archive (offline factory restore, forever),
|
||||||
|
# 2. applies the signed ForgeFIRM .fw to the INACTIVE rootfs slot using
|
||||||
|
# the factory's own fwup (signature-verified),
|
||||||
|
# 3. flips the saved U-Boot environment to the new slot and reboots.
|
||||||
|
# The factory /data partition is never repartitioned or modified beyond
|
||||||
|
# the archive directory; the active factory slot stays bootable.
|
||||||
|
#
|
||||||
|
# Usage: install-forgefirm.sh [local-forgefirm.fw]
|
||||||
|
# With no argument the latest release .fw is downloaded from GitHub.
|
||||||
|
|
||||||
|
RELEASE_FW_URL="https://github.com/ScottW514/forgefirm/releases/latest/download/forgefirm.fw"
|
||||||
|
FFBOOT_URL="https://raw.githubusercontent.com/ScottW514/forgefirm/master/scripts/ffboot"
|
||||||
|
ARCHIVE_DIR="/data/forgefirm/archive"
|
||||||
|
FW_FILE="/data/forgefirm/forgefirm.fw"
|
||||||
|
MIN_DATA_FREE_KB=300000
|
||||||
|
|
||||||
|
# ForgeFIRM release-signing public key (raw 32-byte Ed25519, the format the
|
||||||
|
# factory's fwup 0.14.2 expects).
|
||||||
|
# !! DEV KEY - must be replaced at the production key ceremony !!
|
||||||
|
PUBKEY='\x79\xf5\xc2\x53\x45\x13\x49\x51\xd4\x63\x17\x9d\x60\xd7\x7a\x97\xa7\xd6\xd6\xf4\xd8\x9f\x9d\xbd\x8f\xcc\x28\xfc\xba\xa0\x5d\x11'
|
||||||
|
|
||||||
LIGHTRED="\033[1;31m"
|
LIGHTRED="\033[1;31m"
|
||||||
RED="\033[31m"
|
|
||||||
GREEN="\033[32m"
|
|
||||||
YELLOW="\033[1;33m"
|
YELLOW="\033[1;33m"
|
||||||
BRIGHT="\033[1;39m"
|
BRIGHT="\033[1;39m"
|
||||||
RESET="\033[0m"
|
RESET="\033[0m"
|
||||||
ASTERISK="${LIGHTRED}✺${RESET}"
|
ASTERISK="${LIGHTRED}✺${RESET}"
|
||||||
|
|
||||||
# Abort loudly instead of completing silently broken: a failed step (image
|
|
||||||
# download, flash write, uEnv rewrite) must stop the install.
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo -e "${LIGHTRED}!! INSTALL FAILED:${RESET} $1"
|
echo -e "${LIGHTRED}!! INSTALL FAILED:${RESET} $1"
|
||||||
echo -e "${LIGHTRED}!! The device was NOT fully installed. Fix the problem and re-run.${RESET}"
|
echo -e "${LIGHTRED}!! No boot change was made unless stated otherwise. Fix and re-run.${RESET}"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_gf_services () {
|
stop_gf_services () {
|
||||||
echo -n -e "${ASTERISK}Stopping Glowforge services"
|
echo -n -e "${ASTERISK}Stopping Glowforge services"
|
||||||
sv stop /sv/glowforge 2>&1 >/dev/null; echo -n "."
|
for SVC in glowforge glowforge-datalogger glowforge-updater bugeggs; do
|
||||||
sv stop /sv/glowforge/log 2>&1 >/dev/null; echo -n "."
|
sv stop /sv/$SVC 2>/dev/null >/dev/null; echo -n "."
|
||||||
sv stop /sv/glowforge-datalogger 2>&1 >/dev/null; echo -n "."
|
sv stop /sv/$SVC/log 2>/dev/null >/dev/null; echo -n "."
|
||||||
sv stop /sv/glowforge-datalogger/log 2>&1 >/dev/null; echo -n "."
|
done
|
||||||
sv stop /sv/glowforge-updater 2>&1 >/dev/null; echo -n "."
|
echo "."
|
||||||
sv stop /sv/glowforge-updater/log 2>&1 >/dev/null; echo -n "."
|
}
|
||||||
sv stop /sv/bugeggs 2>&1 >/dev/null; echo -n "."
|
|
||||||
sv stop /sv/bugeggs/log 2>&1 >/dev/null; echo "."
|
# slot_probe <1|2>: sets S_TYPE (factory|forgefirm|unknown) and S_VER
|
||||||
|
slot_probe () {
|
||||||
|
S_TYPE=unknown; S_VER=""
|
||||||
|
MP="/factory/img$1"
|
||||||
|
if [ ! -f "$MP/etc/version" ] && [ ! -f "$MP/etc/forgefirm-version" ]; then
|
||||||
|
mkdir -p "$MP"
|
||||||
|
mount -o ro "/dev/mmcblk2p$1" "$MP" 2>/dev/null
|
||||||
|
fi
|
||||||
|
if [ -f "$MP/etc/forgefirm-version" ]; then
|
||||||
|
S_TYPE=forgefirm; S_VER=$(cat "$MP/etc/forgefirm-version")
|
||||||
|
elif [ -f "$MP/etc/version" ]; then
|
||||||
|
S_TYPE=factory; S_VER=$(cat "$MP/etc/version")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verified atomic env flip (all four variables, classic u-boot-tools script
|
||||||
|
# format first - that is what factory firmware ships - then libubootenv
|
||||||
|
# format, then per-variable writes; read-back verified in every case).
|
||||||
|
FWCONFIG="/etc/fw_env.config"
|
||||||
|
|
||||||
|
env_get () { fw_printenv -c "$FWCONFIG" -n "$1" 2>/dev/null; }
|
||||||
|
|
||||||
|
env_verify () {
|
||||||
|
[ "$(env_get mmcdev)" = "$1" ] && [ "$(env_get mmchwpart)" = "$2" ] && \
|
||||||
|
[ "$(env_get mmcpart)" = "$3" ] && [ "$(env_get mmcroot)" = "$4" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
set_env () {
|
||||||
|
SCRIPT="/tmp/ffinstall.env.$$"
|
||||||
|
printf 'mmcdev %s\nmmchwpart %s\nmmcpart %s\nmmcroot %s\n' "$1" "$2" "$3" "$4" > "$SCRIPT"
|
||||||
|
fw_setenv -c "$FWCONFIG" -s "$SCRIPT" 2>/dev/null
|
||||||
|
if env_verify "$1" "$2" "$3" "$4"; then rm -f "$SCRIPT"; return 0; fi
|
||||||
|
printf 'mmcdev=%s\nmmchwpart=%s\nmmcpart=%s\nmmcroot=%s\n' "$1" "$2" "$3" "$4" > "$SCRIPT"
|
||||||
|
fw_setenv -c "$FWCONFIG" -s "$SCRIPT" 2>/dev/null
|
||||||
|
rm -f "$SCRIPT"
|
||||||
|
if env_verify "$1" "$2" "$3" "$4"; then return 0; fi
|
||||||
|
fw_setenv -c "$FWCONFIG" mmcdev "$1" && \
|
||||||
|
fw_setenv -c "$FWCONFIG" mmchwpart "$2" && \
|
||||||
|
fw_setenv -c "$FWCONFIG" mmcpart "$3" && \
|
||||||
|
fw_setenv -c "$FWCONFIG" mmcroot "$4"
|
||||||
|
env_verify "$1" "$2" "$3" "$4"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo
|
echo
|
||||||
@@ -41,65 +97,29 @@ echo -e "${LIGHTRED} ✺┈┈┈┈┈┈${RESET}"
|
|||||||
echo -e "${BRIGHT}Open${RESET}Glow ForgeFIRM Installation Tool"
|
echo -e "${BRIGHT}Open${RESET}Glow ForgeFIRM Installation Tool"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
if [ -d "/factory" ]; then
|
# --- pre-flight ---------------------------------------------------------------
|
||||||
echo -e "${YELLOW}!! This script must be ran from the factory firmware !!${RESET}"
|
[ -f /etc/version ] && [ -d /glowforge ] \
|
||||||
exit 2
|
|| die "this script must be run from the FACTORY firmware"
|
||||||
fi
|
command -v fwup >/dev/null || die "fwup not found on this system"
|
||||||
|
command -v fw_setenv >/dev/null || die "fw_setenv not found on this system"
|
||||||
|
[ -f "$FWCONFIG" ] || die "$FWCONFIG not found"
|
||||||
|
|
||||||
if [ -d "/ogtmp" ]; then
|
BOOTED=$(sed -n 's/.*root=\([^ ]*\).*/\1/p' /proc/cmdline)
|
||||||
# We're resuming after reboot
|
case "$BOOTED" in
|
||||||
echo -e "${BRIGHT}Starting Stage 2:${RESET}"
|
/dev/mmcblk2p1) ACTIVE=1; TARGET=2; TASK=upgrade.b ;;
|
||||||
|
/dev/mmcblk2p2) ACTIVE=2; TARGET=1; TASK=upgrade.a ;;
|
||||||
|
*) die "booted from $BOOTED - expected factory eMMC slot 1 or 2" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
echo 0 > /proc/sys/kernel/printk
|
for N in 1 2; do
|
||||||
stop_gf_services
|
SZ=$(cat /sys/class/block/mmcblk2p$N/size 2>/dev/null)
|
||||||
|
[ "$SZ" = "409600" ] || die "slot $N is not the 200 MiB factory layout (size=$SZ)"
|
||||||
echo -n -e "${ASTERISK}Restoring /data files"
|
|
||||||
mount -o remount,rw /
|
|
||||||
rm -rf /data/etc; echo -n "."
|
|
||||||
mv /ogtmp/etc /data/; echo -n "."
|
|
||||||
mv /ogtmp/data_pipe.bin /data/; echo -n "."
|
|
||||||
mv /ogtmp/dropbear_rsa_host_key /data/; echo -n "."
|
|
||||||
rm -rf /ogtmp; echo "."
|
|
||||||
|
|
||||||
echo -n -e "${ASTERISK}Restarting network services."
|
|
||||||
/etc/init.d/networking restart 2>&1 >/dev/null
|
|
||||||
for VAR in 1 2 3 4 5; do
|
|
||||||
sleep 2
|
|
||||||
echo -n "."
|
|
||||||
done
|
done
|
||||||
echo
|
|
||||||
|
|
||||||
echo -e "${ASTERISK}Downloading latest OpenGlow/ForgeFIRM image:"
|
FREE_KB=$(df -k /data | tail -1 | awk '{print $4}')
|
||||||
# Asset name matches the Scarthgap deploy artifact verbatim (rootfs.wic.gz),
|
[ "$FREE_KB" -ge "$MIN_DATA_FREE_KB" ] 2>/dev/null \
|
||||||
# so releases are uploaded without renaming (see kas/README.md release order).
|
|| die "need ${MIN_DATA_FREE_KB} KB free on /data, have ${FREE_KB:-unknown}"
|
||||||
curl -fL https://github.com/ScottW514/forgefirm/releases/latest/download/forgefirm-image-glowforge.rootfs.wic.gz --output /data/forgefirm-image-glowforge.rootfs.wic.gz \
|
|
||||||
|| die "image download failed"
|
|
||||||
curl -fL https://raw.githubusercontent.com/ScottW514/forgefirm/master/scripts/ffboot --output /data/ffboot \
|
|
||||||
|| die "ffboot download failed"
|
|
||||||
chmod +x /data/ffboot
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo -e "${ASTERISK}Writing OpenGlow/ForegFIRM image to flash:"
|
|
||||||
zcat /data/forgefirm-image-glowforge.rootfs.wic.gz | dd of=/dev/mmcblk2p4 skip=2 \
|
|
||||||
|| die "flash write to /dev/mmcblk2p4 failed"
|
|
||||||
mkdir -p /data/mnt
|
|
||||||
mount /dev/mmcblk2p4 /data/mnt || die "mounting the new image failed (bad flash write?)"
|
|
||||||
[ -f /data/mnt/boot/uEnv.txt ] \
|
|
||||||
|| { umount /data/mnt; die "/boot/uEnv.txt missing from the new image (bad or outdated image)"; }
|
|
||||||
sed -i 's/mmcblk1p1/mmcblk2p4/g' /data/mnt/boot/uEnv.txt \
|
|
||||||
|| { umount /data/mnt; die "rewriting uEnv.txt for eMMC boot failed"; }
|
|
||||||
umount /data/mnt || die "unmounting the new image failed"
|
|
||||||
rm -rf /data/mnt
|
|
||||||
rm -f /data/forgefirm-image-glowforge.rootfs.wic.gz
|
|
||||||
|
|
||||||
echo 5 > /proc/sys/kernel/printk
|
|
||||||
|
|
||||||
echo -e "${BRIGHT}Stage 2 Complete!${RESET}"
|
|
||||||
echo
|
|
||||||
read -n1 -p "Press any key to reboot into OpenGlow/ForgeFIRM..." continue
|
|
||||||
/data/ffboot -e4
|
|
||||||
else
|
|
||||||
# We're starting fresh
|
|
||||||
echo -e "${LIGHTRED}!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!${RESET}"
|
echo -e "${LIGHTRED}!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!${RESET}"
|
||||||
echo -e "${YELLOW} THIS IS EXPERIMENTAL SOFTWARE!${RESET}"
|
echo -e "${YELLOW} THIS IS EXPERIMENTAL SOFTWARE!${RESET}"
|
||||||
echo -e "The installation and/or use of this software may"
|
echo -e "The installation and/or use of this software may"
|
||||||
@@ -109,67 +129,92 @@ else
|
|||||||
echo -e "endorsed by Glowforge. ${BRIGHT}USE IT AT YOUR OWN RISK!${RESET}"
|
echo -e "endorsed by Glowforge. ${BRIGHT}USE IT AT YOUR OWN RISK!${RESET}"
|
||||||
echo -e "${LIGHTRED}!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!${RESET}"
|
echo -e "${LIGHTRED}!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!${RESET}"
|
||||||
echo
|
echo
|
||||||
|
echo -e "Booted slot: $ACTIVE - ForgeFIRM will be installed to slot $TARGET."
|
||||||
|
echo -e "The factory firmware in slot $ACTIVE stays installed and bootable."
|
||||||
|
echo
|
||||||
read -p "Are you sure you want to continue [N/y]? " continue
|
read -p "Are you sure you want to continue [N/y]? " continue
|
||||||
echo
|
echo
|
||||||
if [ "$continue" != "y" ]; then
|
if [ "$continue" != "y" ]; then
|
||||||
echo "Wise choice. Exiting without changes."
|
echo "Wise choice. Exiting without changes."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
echo -e "${BRIGHT}Starting Stage 1:${RESET}"
|
|
||||||
|
|
||||||
echo 0 > /proc/sys/kernel/printk
|
|
||||||
stop_gf_services
|
stop_gf_services
|
||||||
|
|
||||||
echo -n -e "${ASTERISK}Backing up critical files on /data partition"
|
# --- archive factory content --------------------------------------------------
|
||||||
mount -o remount,rw /; echo -n "."
|
mkdir -p "$ARCHIVE_DIR"
|
||||||
mkdir /ogtmp; echo -n "."
|
for N in 1 2; do
|
||||||
cp -R /data/etc /ogtmp/; echo -n "."
|
slot_probe $N
|
||||||
cp /data/data_pipe.bin /ogtmp/; echo -n "."
|
[ "$S_TYPE" = "factory" ] || continue
|
||||||
cp /data/dropbear_rsa_host_key /ogtmp/; echo "."
|
ARC="$ARCHIVE_DIR/factory-rootfs-$S_VER.img.gz"
|
||||||
|
if [ -s "$ARC" ]; then
|
||||||
|
echo -e "${ASTERISK}Slot $N (factory $S_VER) already archived."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
echo -e "${ASTERISK}Archiving slot $N (factory $S_VER) - takes a few minutes:"
|
||||||
|
dd if=/dev/mmcblk2p$N bs=1M 2>/dev/null | gzip -1 > "$ARC" \
|
||||||
|
|| { rm -f "$ARC"; die "archiving slot $N failed"; }
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M:%S') slot$N factory $S_VER $(basename $ARC) md5=$(md5sum "$ARC" | cut -d' ' -f1)" >> "$ARCHIVE_DIR/manifest"
|
||||||
|
done
|
||||||
|
for B in 0 1; do
|
||||||
|
ARC="$ARCHIVE_DIR/recovery-boot$B.img.gz"
|
||||||
|
[ -s "$ARC" ] && continue
|
||||||
|
echo -e "${ASTERISK}Archiving recovery boot$B:"
|
||||||
|
dd if=/dev/mmcblk2boot$B bs=1M 2>/dev/null | gzip -1 > "$ARC" \
|
||||||
|
|| { rm -f "$ARC"; die "archiving boot$B failed"; }
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M:%S') boot$B recovery - $(basename $ARC) md5=$(md5sum "$ARC" | cut -d' ' -f1)" >> "$ARCHIVE_DIR/manifest"
|
||||||
|
done
|
||||||
|
|
||||||
echo -e "${ASTERISK}Creating partitions (ignore fdisk warning):"
|
# --- acquire the ForgeFIRM .fw ------------------------------------------------
|
||||||
umount -l /dev/mmcblk2p3
|
mkdir -p /data/forgefirm
|
||||||
sed -e 's/\s*\([\+0-9a-zA-Z]*\).*/\1/' << EOF | fdisk /dev/mmcblk2 2>&1 >/dev/null
|
if [ -n "$1" ]; then
|
||||||
o # Removing existing partitions
|
[ -s "$1" ] || die "local firmware file '$1' not found"
|
||||||
n # New partition
|
cp "$1" "$FW_FILE"
|
||||||
p # Primary partition
|
echo -e "${ASTERISK}Using local firmware file: $1"
|
||||||
1 # Creating new /data partition
|
else
|
||||||
129 # Start at the end of partition 2
|
echo -e "${ASTERISK}Downloading latest OpenGlow/ForgeFIRM release:"
|
||||||
6528 # 2GB /data parttion
|
curl -fL "$RELEASE_FW_URL" --output "$FW_FILE" || die "firmware download failed"
|
||||||
n # New partition
|
|
||||||
p # Primary partition
|
|
||||||
2 # Creating new /data partition
|
|
||||||
6529 # Start at the end of partition 2
|
|
||||||
12928 # 2GB /data parttion
|
|
||||||
n # New partition
|
|
||||||
p # Primary partition
|
|
||||||
3 # Creating new /data partition
|
|
||||||
12929 # Start at the end of partition 2
|
|
||||||
+2048M # 2GB /data parttion
|
|
||||||
n # New partition for OpenGlow
|
|
||||||
p # Primary partition (Will automatically select 4)
|
|
||||||
75430 # Start at the end of partition 3
|
|
||||||
# default, Use remaining space
|
|
||||||
w # write the partition table
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# We need to clear the original partition of filesystem data
|
|
||||||
# This way the Glowforge will properly recreate the /data partition
|
|
||||||
# on the next reboot.
|
|
||||||
dd if=/dev/zero of=/dev/mmcblk2p3 bs=4096 count=4096 2>&1 >/dev/null
|
|
||||||
|
|
||||||
echo -e "${BRIGHT}Completed Stage 1${RESET}"
|
|
||||||
echo
|
|
||||||
echo -e "The device needs to reboot."
|
|
||||||
echo -e "After the device has rebooted, run this script"
|
|
||||||
echo -e "to continue the installation process."
|
|
||||||
echo
|
|
||||||
echo 5 > /proc/sys/kernel/printk
|
|
||||||
read -n1 -p "Press any key to reboot..." continue
|
|
||||||
echo
|
|
||||||
echo "Rebooting..."
|
|
||||||
reboot
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# --- verify signature ---------------------------------------------------------
|
||||||
|
KEYFILE="/tmp/forgefirm.pub.$$"
|
||||||
|
printf "$PUBKEY" > "$KEYFILE"
|
||||||
|
[ "$(wc -c < "$KEYFILE")" = "32" ] || die "embedded public key corrupt"
|
||||||
|
echo -e "${ASTERISK}Verifying firmware signature:"
|
||||||
|
fwup -V -i "$FW_FILE" -p "$KEYFILE" || { rm -f "$KEYFILE"; die "signature verification FAILED - refusing to install"; }
|
||||||
|
fwup -m -i "$FW_FILE" | grep meta-version
|
||||||
|
|
||||||
|
# --- apply to the inactive slot -----------------------------------------------
|
||||||
|
echo -e "${ASTERISK}Writing ForgeFIRM to slot $TARGET (/dev/mmcblk2p$TARGET):"
|
||||||
|
umount "/factory/img$TARGET" 2>/dev/null
|
||||||
|
fwup -a -d "/dev/mmcblk2p$TARGET" -i "$FW_FILE" -t "$TASK" -p "$KEYFILE" \
|
||||||
|
|| { rm -f "$KEYFILE"; die "fwup apply failed - slot $TARGET is now undefined, factory slot $ACTIVE is untouched"; }
|
||||||
|
rm -f "$KEYFILE"
|
||||||
|
|
||||||
|
# --- post-write verify --------------------------------------------------------
|
||||||
|
MP="/factory/img$TARGET"
|
||||||
|
mkdir -p "$MP"
|
||||||
|
mount -o ro "/dev/mmcblk2p$TARGET" "$MP" || die "new rootfs does not mount"
|
||||||
|
NEWVER=$(cat "$MP/etc/forgefirm-version" 2>/dev/null)
|
||||||
|
[ -n "$NEWVER" ] || { umount "$MP"; die "new rootfs has no ForgeFIRM version stamp"; }
|
||||||
|
[ -f "$MP/boot/zImage" ] || { umount "$MP"; die "new rootfs has no kernel"; }
|
||||||
|
umount "$MP"
|
||||||
|
echo -e "${ASTERISK}Slot $TARGET now holds ForgeFIRM $NEWVER"
|
||||||
|
|
||||||
|
# --- ffboot for the factory side ----------------------------------------------
|
||||||
|
curl -fL "$FFBOOT_URL" --output /data/ffboot || die "ffboot download failed"
|
||||||
|
chmod +x /data/ffboot
|
||||||
|
|
||||||
|
# --- flip the boot selection --------------------------------------------------
|
||||||
|
echo -e "${ASTERISK}Setting boot to /dev/mmcblk2p$TARGET"
|
||||||
|
set_env 1 0 "$TARGET" "/dev/mmcblk2p$TARGET" \
|
||||||
|
|| die "environment write did not verify - boot selection unchanged; run /data/ffboot -e$TARGET manually"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
echo -e "${BRIGHT}Installation complete.${RESET}"
|
||||||
|
echo -e "To return to factory firmware later: ${BRIGHT}/data/ffboot -e${RESET} (from ForgeFIRM: ${BRIGHT}ffboot -e${RESET})"
|
||||||
|
echo
|
||||||
|
read -n1 -p "Press any key to reboot into OpenGlow/ForgeFIRM..." continue
|
||||||
|
echo
|
||||||
|
reboot
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
Reference in New Issue
Block a user