diff --git a/docs/UPDATE-SYSTEM.md b/docs/UPDATE-SYSTEM.md index 8ac6add..0187853 100644 --- a/docs/UPDATE-SYSTEM.md +++ b/docs/UPDATE-SYSTEM.md @@ -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-.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-.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-.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-.fw` (signed; tasks `upgrade.a`/`upgrade.b`, + `forgefirm.fw` (fixed asset name; signed; version in the fwup + metadata = release tag `v`; tasks `upgrade.a`/`upgrade.b`, `complete` from Phase 5), `sha256sums.txt`, - `forgefirm-image-glowforge.rootfs.wic.gz` (SD burns), release tag - `v`. + `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/` — diff --git a/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb b/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb index 137900b..93b9444 100644 --- a/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb +++ b/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb @@ -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 — diff --git a/meta-forgefirm/recipes-forgefirm/slotmigrate/files/slotmigrate b/meta-forgefirm/recipes-forgefirm/slotmigrate/files/slotmigrate new file mode 100644 index 0000000..1d911d2 --- /dev/null +++ b/meta-forgefirm/recipes-forgefirm/slotmigrate/files/slotmigrate @@ -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 diff --git a/meta-forgefirm/recipes-forgefirm/slotmigrate/slotmigrate.bb b/meta-forgefirm/recipes-forgefirm/slotmigrate/slotmigrate.bb new file mode 100644 index 0000000..cf2d00a --- /dev/null +++ b/meta-forgefirm/recipes-forgefirm/slotmigrate/slotmigrate.bb @@ -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 +} diff --git a/scripts/install-forgefirm.sh b/scripts/install-forgefirm.sh index bd38a7b..372dace 100644 --- a/scripts/install-forgefirm.sh +++ b/scripts/install-forgefirm.sh @@ -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