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:
ScottW514
2026-08-08 12:26:33 -04:00
parent 5035b2744e
commit 1097184e13
5 changed files with 313 additions and 152 deletions
+17 -13
View File
@@ -101,16 +101,20 @@ factory firmware:
1. Sanity: factory 3-partition layout, both slots 200 MiB, active slot
detected (`rdev`), enough `/data` space.
2. Archive: identify the **newer** factory version of the two slots;
`dd | gzip` it to `/data/forgefirm/archive/factory-rootfs-<ver>.img.gz`
with a manifest (versions of both slots, date); also dump
boot0/boot1 (32 MiB) into the archive now, ahead of Phase 5.
3. Fetch `forgefirm-<ver>.fw` from GitHub releases (or take a local
file argument for offline/dev installs).
4. Apply to the slot holding the **older** factory version (fwup +
our pubkey). The newer factory install stays bootable in the other
slot *and* is archived — so the first ForgeFIRM self-upgrade may
overwrite it without a second archive step.
2. Archive: **every factory slot version** not already archived —
`dd | gzip` to `/data/forgefirm/archive/factory-rootfs-<ver>.img.gz`
with a manifest line (slot, version, date, md5); also dump
boot0/boot1 (32 MiB) into the archive now, ahead of Phase 5. With
both slots archived, any later overwrite needs no second archive
step.
3. Fetch `forgefirm.fw` from GitHub releases (fixed asset name — the
`releases/latest/download/` URL needs one; the version lives in the
fwup metadata and the release tag), or take a local file argument
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
ffboot v2), reboot.
@@ -202,10 +206,10 @@ selector, factory restore and return — without touching a shell.*
## Contracts
- **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`,
`forgefirm-image-glowforge.rootfs.wic.gz` (SD burns), release tag
`v<semver>`.
`forgefirm-image-glowforge.rootfs.wic.gz` (SD burns).
- **Env**: SD = `0/0/1//dev/mmcblk1p1`; slot N = `1/0/N//dev/mmcblk2pN`
(`mmcdev/mmchwpart/mmcpart/mmcroot`, always one transaction).
- **Archive layout**: `/data/forgefirm/archive/` —
@@ -19,7 +19,8 @@ IMAGE_INSTALL:remove = "gfui-client"
# fwup: applies signed .fw archives (ForgeFIRM upgrades + factory restore)
# to the inactive rootfs slot.
# 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).
# 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
}
+183 -138
View File
@@ -4,36 +4,92 @@
# https://community.openglow.org
# SPDX-License-Identifier: MIT
#
# This program makes a partition on the built-in eMMC flash storage on
# the factory Glowforge, and installs the latest copy of OpenGlow/ForgeFIRM.
# Single-stage OpenGlow/ForgeFIRM installer. Runs on FACTORY firmware:
# 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"
RED="\033[31m"
GREEN="\033[32m"
YELLOW="\033[1;33m"
BRIGHT="\033[1;39m"
RESET="\033[0m"
ASTERISK="${LIGHTRED}✺${RESET}"
# Abort loudly instead of completing silently broken: a failed step (image
# download, flash write, uEnv rewrite) must stop the install.
die () {
echo
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
}
stop_gf_services () {
echo -n -e "${ASTERISK}Stopping Glowforge services"
sv stop /sv/glowforge 2>&1 >/dev/null; echo -n "."
sv stop /sv/glowforge/log 2>&1 >/dev/null; echo -n "."
sv stop /sv/glowforge-datalogger 2>&1 >/dev/null; echo -n "."
sv stop /sv/glowforge-datalogger/log 2>&1 >/dev/null; echo -n "."
sv stop /sv/glowforge-updater 2>&1 >/dev/null; echo -n "."
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 "."
for SVC in glowforge glowforge-datalogger glowforge-updater bugeggs; do
sv stop /sv/$SVC 2>/dev/null >/dev/null; echo -n "."
sv stop /sv/$SVC/log 2>/dev/null >/dev/null; echo -n "."
done
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
@@ -41,135 +97,124 @@ echo -e "${LIGHTRED} ✺┈┈┈┈┈┈${RESET}"
echo -e "${BRIGHT}Open${RESET}Glow ForgeFIRM Installation Tool"
echo
if [ -d "/factory" ]; then
echo -e "${YELLOW}!! This script must be ran from the factory firmware !!${RESET}"
exit 2
# --- pre-flight ---------------------------------------------------------------
[ -f /etc/version ] && [ -d /glowforge ] \
|| die "this script must be run from the FACTORY firmware"
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"
BOOTED=$(sed -n 's/.*root=\([^ ]*\).*/\1/p' /proc/cmdline)
case "$BOOTED" in
/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
for N in 1 2; do
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)"
done
FREE_KB=$(df -k /data | tail -1 | awk '{print $4}')
[ "$FREE_KB" -ge "$MIN_DATA_FREE_KB" ] 2>/dev/null \
|| die "need ${MIN_DATA_FREE_KB} KB free on /data, have ${FREE_KB:-unknown}"
echo -e "${LIGHTRED}!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!${RESET}"
echo -e "${YELLOW} THIS IS EXPERIMENTAL SOFTWARE!${RESET}"
echo -e "The installation and/or use of this software may"
echo -e "result in damage to your device and/or property,"
echo -e "loss of warranty, and severe bodily injury and/or"
echo -e "death. This software is not affiliated with or"
echo -e "endorsed by Glowforge. ${BRIGHT}USE IT AT YOUR OWN RISK!${RESET}"
echo -e "${LIGHTRED}!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!${RESET}"
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
echo
if [ "$continue" != "y" ]; then
echo "Wise choice. Exiting without changes."
exit 0
fi
if [ -d "/ogtmp" ]; then
# We're resuming after reboot
echo -e "${BRIGHT}Starting Stage 2:${RESET}"
stop_gf_services
echo 0 > /proc/sys/kernel/printk
stop_gf_services
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
echo
echo -e "${ASTERISK}Downloading latest OpenGlow/ForgeFIRM image:"
# Asset name matches the Scarthgap deploy artifact verbatim (rootfs.wic.gz),
# so releases are uploaded without renaming (see kas/README.md release order).
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 "${YELLOW} THIS IS EXPERIMENTAL SOFTWARE!${RESET}"
echo -e "The installation and/or use of this software may"
echo -e "result in damage to your device and/or property,"
echo -e "loss of warranty, and severe bodily injury and/or"
echo -e "death. This software is not affiliated with or"
echo -e "endorsed by Glowforge. ${BRIGHT}USE IT AT YOUR OWN RISK!${RESET}"
echo -e "${LIGHTRED}!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!${RESET}"
echo
read -p "Are you sure you want to continue [N/y]? " continue
echo
if [ "$continue" != "y" ]; then
echo "Wise choice. Exiting without changes."
exit 0
# --- archive factory content --------------------------------------------------
mkdir -p "$ARCHIVE_DIR"
for N in 1 2; do
slot_probe $N
[ "$S_TYPE" = "factory" ] || continue
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 "${BRIGHT}Starting Stage 1:${RESET}"
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 0 > /proc/sys/kernel/printk
stop_gf_services
echo -n -e "${ASTERISK}Backing up critical files on /data partition"
mount -o remount,rw /; echo -n "."
mkdir /ogtmp; echo -n "."
cp -R /data/etc /ogtmp/; echo -n "."
cp /data/data_pipe.bin /ogtmp/; echo -n "."
cp /data/dropbear_rsa_host_key /ogtmp/; echo "."
echo -e "${ASTERISK}Creating partitions (ignore fdisk warning):"
umount -l /dev/mmcblk2p3
sed -e 's/\s*\([\+0-9a-zA-Z]*\).*/\1/' << EOF | fdisk /dev/mmcblk2 2>&1 >/dev/null
o # Removing existing partitions
n # New partition
p # Primary partition
1 # Creating new /data partition
129 # Start at the end of partition 2
6528 # 2GB /data parttion
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
# --- acquire the ForgeFIRM .fw ------------------------------------------------
mkdir -p /data/forgefirm
if [ -n "$1" ]; then
[ -s "$1" ] || die "local firmware file '$1' not found"
cp "$1" "$FW_FILE"
echo -e "${ASTERISK}Using local firmware file: $1"
else
echo -e "${ASTERISK}Downloading latest OpenGlow/ForgeFIRM release:"
curl -fL "$RELEASE_FW_URL" --output "$FW_FILE" || die "firmware download failed"
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 -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