diff --git a/docs/BRINGUP.md b/docs/BRINGUP.md index 4dc6eb5..2d99a6a 100644 --- a/docs/BRINGUP.md +++ b/docs/BRINGUP.md @@ -113,6 +113,15 @@ motion constants were extracted from the `_RESOURCES` pulse files kas/forgefirm-glowforge.yml -c 'bitbake forgefirm-image forgefirm-image-dev'`. Artifacts: `forgefirm/build/tmp/deploy/images/glowforge/`. +- **fwup lab (host)**: `~/fwup-lab/bin/` holds host-built `fwup-0.14.2` + (factory-era) and `fwup-v1.16.0`; `~/fwup-lab/devkeys/fwup-key.{priv,pub}` + is the DEV signing keypair (`fwup-key-raw.pub` = raw 32-byte form — + what fwup 0.14.2 expects; 1.x reads both). Cross-version compat is + proven both ways (modern-packed signed archives apply with 0.14.2; + modern fwup verifies+applies the factory .fw — signer key + 2017-05-001.pub). The production release key does not exist yet — + generation/custody is an operator ceremony (UPDATE-SYSTEM.md gate 3). + Pack releases with `scripts/mkfw.sh`. - **Shell gotchas** (cost real time): PowerShell mangles embedded double quotes in git-commit here-strings (avoid `"` in messages); `wsl -- bash -c '...'` eats `$VAR` expansions (use script files run via PowerShell, diff --git a/meta-forgefirm/recipes-extended/fwup/fwup_1.16.0.bb b/meta-forgefirm/recipes-extended/fwup/fwup_1.16.0.bb new file mode 100644 index 0000000..987c244 --- /dev/null +++ b/meta-forgefirm/recipes-extended/fwup/fwup_1.16.0.bb @@ -0,0 +1,14 @@ +SUMMARY = "Configurable embedded Linux firmware update creator and runner" +DESCRIPTION = "Applies and creates signed .fw firmware archives. ForgeFIRM \ +uses the factory's own update format: fwup applies ForgeFIRM upgrades and \ +Glowforge factory-restore archives to the inactive rootfs slot." +HOMEPAGE = "https://github.com/fwup-home/fwup" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57" + +DEPENDS = "libconfuse libarchive libsodium" + +SRC_URI = "https://github.com/fwup-home/fwup/releases/download/v${PV}/fwup-${PV}.tar.gz" +SRC_URI[sha256sum] = "a07b79268247ecee134a916ab928914be2a4ecbac0bc5e5f19212ec36ecb5c21" + +inherit autotools pkgconfig bash-completion diff --git a/meta-forgefirm/recipes-forgefirm/images/forgefirm-image-dev.bb b/meta-forgefirm/recipes-forgefirm/images/forgefirm-image-dev.bb index 364bee1..31b5852 100644 --- a/meta-forgefirm/recipes-forgefirm/images/forgefirm-image-dev.bb +++ b/meta-forgefirm/recipes-forgefirm/images/forgefirm-image-dev.bb @@ -12,6 +12,11 @@ IMAGE_FEATURES += " \ tools-debug \ " +# Dev images boot from SD, never from a 200 MiB eMMC slot: lift the slot +# ceiling and give the filesystem generous working space instead. +IMAGE_ROOTFS_MAXSIZE = "" +IMAGE_ROOTFS_EXTRA_SPACE = "262144" + # Dev builds identify by build timestamp (matches the artifact name), # tagged so a bench machine is never mistaken for a release. FORGEFIRM_VERSION_STRING = "${DATETIME} (dev)" diff --git a/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb b/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb index 7e67070..293a9e8 100644 --- a/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb +++ b/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb @@ -16,7 +16,19 @@ IMAGE_INSTALL:remove = "gfui-client" # for $H when homing_mode = gfcloud (/data/forgefirm.conf). # v4l-utils provides media-ctl / v4l2-ctl for the imx-media pipeline (also a # forgectrl runtime dependency, kept explicit here for bring-up use). -IMAGE_INSTALL:append = " grblhal-glowforge forgectrl gfhome v4l-utils" +# fwup: applies signed .fw archives (ForgeFIRM upgrades + factory restore) +# to the inactive rootfs slot. +IMAGE_INSTALL:append = " grblhal-glowforge forgectrl gfhome v4l-utils fwup" + +# 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 — +# the build fails rather than emit an unflashable image. The raw ext4 is +# deployed alongside the wic; scripts/mkfw.sh packs it into the signed +# .fw release artifact. +IMAGE_FSTYPES:append = " ext4" +IMAGE_OVERHEAD_FACTOR = "1.0" +IMAGE_ROOTFS_EXTRA_SPACE = "40960" +IMAGE_ROOTFS_MAXSIZE = "204800" # Version stamp: /etc/forgefirm-version (machine-readable), echoed on the # serial-console login prompt (/etc/issue) and at SSH login (motd). diff --git a/scripts/mkfw.sh b/scripts/mkfw.sh new file mode 100644 index 0000000..f0358a4 --- /dev/null +++ b/scripts/mkfw.sh @@ -0,0 +1,85 @@ +#!/bin/sh +# (C) Copyright 2020-2026 +# Scott Wiederhold, s.e.wiederhold@gmail.com +# https://community.openglow.org +# SPDX-License-Identifier: MIT +# +# Packs a ForgeFIRM rootfs.ext4 into a fwup .fw archive with the factory +# update task layout: upgrade.a / upgrade.b raw-write the rootfs into eMMC +# slot p1 / p2 (fwup is invoked with -d /dev/mmcblk2p, so offsets are +# partition-relative, exactly like the factory updater). +# +# Usage: mkfw.sh [private-key] +# FWUP= overrides the fwup binary (any 1.x; archives verified to +# apply with the factory's fwup 0.14.2). +# Unsigned output (no key) is for dev only: release and updater paths +# require a signature. + +set -e + +FWUP="${FWUP:-fwup}" +ROOTFS="$1" +VERSION="$2" +OUT="$3" +KEY="$4" + +usage () { + echo "usage: mkfw.sh [private-key]" >&2 + exit 2 +} + +[ -n "$ROOTFS" ] && [ -n "$VERSION" ] && [ -n "$OUT" ] || usage +[ -f "$ROOTFS" ] || { echo "ERROR: rootfs '$ROOTFS' not found" >&2; exit 1; } +command -v "$FWUP" >/dev/null || { echo "ERROR: fwup not found (set FWUP=)" >&2; exit 1; } + +# The rootfs must fit the 200 MiB factory slot (409600 x 512-byte blocks). +SLOT_BYTES=209715200 +SIZE=$(wc -c < "$ROOTFS") +[ "$SIZE" -le "$SLOT_BYTES" ] || { + echo "ERROR: rootfs is $SIZE bytes; slot holds $SLOT_BYTES" >&2; exit 1; } + +WORK=$(mktemp -d) +trap 'rm -rf "$WORK"' EXIT +cp "$ROOTFS" "$WORK/rootfs.ext4" + +cat > "$WORK/fwup.conf" <&2; exit 1; } + "$FWUP" -S -s "$KEY" -i "$WORK/unsigned.fw" -o "$OUT" + PUB="${KEY%.priv}.pub" + if [ -f "$PUB" ]; then + "$FWUP" -V -i "$OUT" -p "$PUB" || { echo "ERROR: signature self-check failed" >&2; exit 1; } + fi + echo "signed: $OUT" +else + cp "$WORK/unsigned.fw" "$OUT" + echo "UNSIGNED (dev only): $OUT" +fi + +"$FWUP" -m -i "$OUT" | head -4