mkdiskimage.sh --starship-from IDENT ART lays a minted pair out as a StarshipOS stick: an identity partition holding IDENT's bytes and an Artemis partition holding ART's, each exactly its file's size (Artemis's signature and fence are placed from the volume's size). kernel/Makefile: a zusestick target, made fresh for every boot, is ZUSEDISK's default (ZUSE_PAIR names the pair); STICK_PAIR puts the pair in the PC stick. qemu_session.py's Zuse drive is the pair as a stick. check_images.sh checks both forms byte for byte against the pair (60 PASS). On today's kernel all 23 sessions pass on the Zuse stick, and acceptance: amd64, aarch64, riscv64 to [zuse@Hera] ok>, POST 538/538, dict_hash 0xfec8c47cfc6f437f; v4 suite and test-32 0 failures. The plan gains Task 5, screen=boot (ruled 2026-10-28). Plan: docs/v4.0.0/plans/2026-10-27-stick-4-one-disk.md, Task 1 (R1 as recommended). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
162 lines
7.5 KiB
Bash
162 lines
7.5 KiB
Bash
#!/usr/bin/env bash
|
|
# mkdiskimage.sh -- build a single-partition FAT32 boot disk image.
|
|
#
|
|
# Used by `make boot_image TARGET=<board>` (kernel/Makefile). One script for
|
|
# every board so the geometry rules live in one place:
|
|
# - partition 1 starts at 1 MiB (sector 2048) and runs to the last usable
|
|
# sector; the FAT32 filesystem is sized to the partition exactly (a
|
|
# larger filesystem overruns the partition -- the bug that made early
|
|
# SER5 thumbdrive images unbootable, see kernel/Makefile `thumbdrive`);
|
|
# - GPT tables reserve 33 sectors at the end of the disk for the backup
|
|
# header, MBR tables do not.
|
|
#
|
|
# Usage:
|
|
# mkdiskimage.sh --out IMG --table gpt|mbr --size-mib N --label LABEL \
|
|
# SRC:DEST [SRC:DEST ...]
|
|
#
|
|
# --table gpt -> partition type "EFI System" (UEFI firmware, U-Boot bootefi)
|
|
#
|
|
# The StarshipOS stick and data stick (docs/v4.0.0/STICK.md 4; --table gpt only):
|
|
# --starship IDENT_MIB --mkartemis TOOL --esp-mib E
|
|
# lays out an ESP of E MiB holding the SRC:DEST files (E=0: no ESP, no files),
|
|
# then a StarshipOS identity partition of IDENT_MIB MiB (all zeros), then a
|
|
# StarshipOS Artemis partition to the last whole MiB before the backup GPT,
|
|
# formatted and signed by TOOL (tools/mkartemis.c). Every partition starts and
|
|
# ends on a whole MiB, so on 1 KiB blocks, as the kernel's GPT reader needs.
|
|
#
|
|
# Zuse's minted pair as a stick (docs/v4.0.0/plans/2026-10-27-stick-4-one-disk.md, Task 1):
|
|
# --starship-from IDENT_IMG ART_IMG --esp-mib E (no --size-mib, no --mkartemis)
|
|
# the same layout, but the identity partition is exactly IDENT_IMG's size and holds its bytes,
|
|
# and the Artemis partition is exactly ART_IMG's size and holds its bytes (Artemis's signature
|
|
# and fence are placed from the volume's size, so it cannot grow). Each must be a whole MiB.
|
|
# The image is 1 + E + |IDENT_IMG| + |ART_IMG| + 1 MiB.
|
|
# --table mbr -> partition type 0x0c FAT32 (LBA) (Raspberry Pi firmware)
|
|
# DEST is an absolute path inside the FAT filesystem; parent directories are
|
|
# created as needed.
|
|
#
|
|
# Needs only sfdisk (util-linux), mkfs.fat (dosfstools) and mtools -- no
|
|
# root, no loop devices.
|
|
set -euo pipefail
|
|
|
|
OUT="" TABLE="" SIZE_MIB="" LABEL="" IDENT_MIB="" MKARTEMIS="" ESP_MIB="" FROM_IDENT="" FROM_ART=""
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--out) OUT="$2"; shift 2 ;;
|
|
--table) TABLE="$2"; shift 2 ;;
|
|
--size-mib) SIZE_MIB="$2"; shift 2 ;;
|
|
--label) LABEL="$2"; shift 2 ;;
|
|
--starship) IDENT_MIB="$2"; shift 2 ;;
|
|
--mkartemis) MKARTEMIS="$2"; shift 2 ;;
|
|
--esp-mib) ESP_MIB="$2"; shift 2 ;;
|
|
--starship-from) FROM_IDENT="$2"; FROM_ART="$3"; shift 3 ;;
|
|
--) shift; break ;;
|
|
-*) echo "mkdiskimage.sh: unknown option $1" >&2; exit 2 ;;
|
|
*) break ;;
|
|
esac
|
|
done
|
|
|
|
if [ -n "$FROM_IDENT" ]; then
|
|
[ "$TABLE" = gpt ] && [ -n "$ESP_MIB" ] && [ -z "$SIZE_MIB" ] && [ -z "$MKARTEMIS" ] || {
|
|
echo "mkdiskimage.sh: --starship-from needs --table gpt and --esp-mib, and no --size-mib or --mkartemis" >&2; exit 2; }
|
|
for f in "$FROM_IDENT" "$FROM_ART"; do
|
|
[ -f "$f" ] || { echo "mkdiskimage.sh: missing input $f" >&2; exit 1; }
|
|
[ $(( $(stat -c %s "$f") % 1048576 )) -eq 0 ] || { echo "mkdiskimage.sh: $f is not a whole MiB" >&2; exit 2; }
|
|
done
|
|
IDENT_MIB=$(( $(stat -c %s "$FROM_IDENT") / 1048576 ))
|
|
FROM_ART_MIB=$(( $(stat -c %s "$FROM_ART") / 1048576 ))
|
|
SIZE_MIB=$(( 1 + ESP_MIB + IDENT_MIB + FROM_ART_MIB + 1 ))
|
|
MKARTEMIS=-
|
|
fi
|
|
[ -n "$OUT" ] && [ -n "$TABLE" ] && [ -n "$SIZE_MIB" ] && [ -n "$LABEL" ] || {
|
|
echo "mkdiskimage.sh: --out, --table, --size-mib and --label are required" >&2; exit 2; }
|
|
if [ -n "$IDENT_MIB" ]; then
|
|
[ "$TABLE" = gpt ] && [ -n "$MKARTEMIS" ] && [ -n "$ESP_MIB" ] || {
|
|
echo "mkdiskimage.sh: --starship needs --table gpt, --mkartemis and --esp-mib" >&2; exit 2; }
|
|
[ "$ESP_MIB" -gt 0 ] || [ $# -eq 0 ] || { echo "mkdiskimage.sh: --esp-mib 0 takes no files" >&2; exit 2; }
|
|
fi
|
|
[ $# -gt 0 ] || [ "${ESP_MIB:-}" = 0 ] || { echo "mkdiskimage.sh: no SRC:DEST files given" >&2; exit 2; }
|
|
|
|
for tool in sfdisk mkfs.fat mmd mcopy; do
|
|
command -v "$tool" >/dev/null 2>&1 || PATH="$PATH:/usr/sbin:/sbin"
|
|
command -v "$tool" >/dev/null 2>&1 || {
|
|
echo "mkdiskimage.sh: $tool not found (install util-linux, dosfstools, mtools)" >&2; exit 1; }
|
|
done
|
|
|
|
TOTAL_SECTORS=$(( SIZE_MIB * 2048 ))
|
|
START=2048
|
|
case "$TABLE" in
|
|
gpt) LAST=$(( TOTAL_SECTORS - 34 )); PTYPE="C12A7328-F81F-11D2-BA4B-00A0C93EC93B" ;;
|
|
mbr) LAST=$(( TOTAL_SECTORS - 1 )); PTYPE="c" ;;
|
|
*) echo "mkdiskimage.sh: --table must be gpt or mbr" >&2; exit 2 ;;
|
|
esac
|
|
PART_SECTORS=$(( LAST - START + 1 ))
|
|
# StarshipOS partition types (kernel/src/gpt.c)
|
|
IDENT_TYPE="23BEBFE1-4817-4CF3-A537-9069BF159788"
|
|
ART_TYPE="BCD7B586-1C8E-4EEF-86A3-273A8D8EE624"
|
|
if [ -n "$IDENT_MIB" ]; then
|
|
PART_SECTORS=$(( ESP_MIB * 2048 ))
|
|
IDENT_START=$(( START + PART_SECTORS ))
|
|
IDENT_SECTORS=$(( IDENT_MIB * 2048 ))
|
|
ART_START=$(( IDENT_START + IDENT_SECTORS ))
|
|
ART_SECTORS=$(( (LAST + 1 - ART_START) / 2048 * 2048 ))
|
|
[ -z "$FROM_ART" ] || ART_SECTORS=$(( FROM_ART_MIB * 2048 ))
|
|
[ "$ART_SECTORS" -gt 0 ] || { echo "mkdiskimage.sh: no room for Artemis in ${SIZE_MIB} MiB" >&2; exit 2; }
|
|
fi
|
|
|
|
ESP="$OUT.part1"
|
|
rm -f "$OUT" "$ESP"
|
|
mkdir -p "$(dirname "$OUT")"
|
|
|
|
truncate -s $(( TOTAL_SECTORS * 512 )) "$OUT"
|
|
if [ -n "$IDENT_MIB" ]; then
|
|
{
|
|
echo "label: gpt"
|
|
[ "$ESP_MIB" -gt 0 ] && printf 'start=%d, size=%d, type=%s, name="EFI System"\n' "$START" "$PART_SECTORS" "$PTYPE"
|
|
printf 'start=%d, size=%d, type=%s, name="StarshipOS identity"\n' "$IDENT_START" "$IDENT_SECTORS" "$IDENT_TYPE"
|
|
printf 'start=%d, size=%d, type=%s, name="StarshipOS Artemis"\n' "$ART_START" "$ART_SECTORS" "$ART_TYPE"
|
|
} | sfdisk --quiet "$OUT"
|
|
if [ -n "$FROM_IDENT" ]; then
|
|
dd if="$FROM_IDENT" of="$OUT" bs=1M seek=$(( IDENT_START / 2048 )) conv=notrunc,sparse status=none
|
|
dd if="$FROM_ART" of="$OUT" bs=1M seek=$(( ART_START / 2048 )) conv=notrunc,sparse status=none
|
|
else
|
|
"$MKARTEMIS" --format "$OUT" --first-lba "$ART_START" --sectors "$ART_SECTORS" >/dev/null || {
|
|
echo "mkdiskimage.sh: formatting Artemis failed" >&2; rm -f "$OUT"; exit 1; }
|
|
fi
|
|
if [ "$ESP_MIB" = 0 ]; then
|
|
echo " IMAGE $OUT (gpt, ${SIZE_MIB} MiB: StarshipOS identity ${IDENT_MIB} MiB, Artemis $(( ART_SECTORS / 2048 )) MiB)"
|
|
exit 0
|
|
fi
|
|
elif [ "$TABLE" = gpt ]; then
|
|
printf 'label: gpt\nstart=%d, size=%d, type=%s, name="EFI System"\n' \
|
|
"$START" "$PART_SECTORS" "$PTYPE" | sfdisk --quiet "$OUT"
|
|
else
|
|
printf 'label: dos\nstart=%d, size=%d, type=%s, bootable\n' \
|
|
"$START" "$PART_SECTORS" "$PTYPE" | sfdisk --quiet "$OUT"
|
|
fi
|
|
|
|
truncate -s $(( PART_SECTORS * 512 )) "$ESP"
|
|
mkfs.fat -F 32 -n "$LABEL" "$ESP" >/dev/null
|
|
|
|
for spec in "$@"; do
|
|
src="${spec%%:*}"
|
|
dest="${spec#*:}"
|
|
[ -f "$src" ] || { echo "mkdiskimage.sh: missing input $src" >&2; rm -f "$OUT" "$ESP"; exit 1; }
|
|
dir="$(dirname "$dest")"
|
|
path=""
|
|
IFS=/ read -r -a parts <<< "${dir#/}"
|
|
for p in "${parts[@]}"; do
|
|
[ -n "$p" ] || continue
|
|
path="$path/$p"
|
|
mmd -D s -i "$ESP" "::$path" 2>/dev/null || true
|
|
done
|
|
mcopy -o -i "$ESP" "$src" "::$dest"
|
|
done
|
|
|
|
dd if="$ESP" of="$OUT" bs=512 seek="$START" conv=notrunc status=none
|
|
rm -f "$ESP"
|
|
if [ -n "$IDENT_MIB" ]; then
|
|
echo " IMAGE $OUT (gpt, ${SIZE_MIB} MiB: ESP ${ESP_MIB} MiB FAT32 '$LABEL', StarshipOS identity ${IDENT_MIB} MiB, Artemis $(( ART_SECTORS / 2048 )) MiB)"
|
|
else
|
|
echo " IMAGE $OUT ($TABLE, ${SIZE_MIB} MiB, FAT32 '$LABEL')"
|
|
fi
|