feat(build): the stick and the data stick, Artemis formatted and signed at build
The stick's increment 3, Task 1 (docs/v4.0.0/plans/2026-10-27-stick-3-images.md; STICK.md 4). make boot_image TARGET=PC now writes the 2 GiB stick: ESP 64 MiB (BOOTX64.EFI, startup.nsh, starforth.cfg), StarshipOS identity 500 MiB (zeros), StarshipOS Artemis to the end. make -f kernel/Makefile ARCH=<arch> datastick writes the 1.5 GiB data stick, identity and Artemis, fresh every time. Sizes in kernel/Makefile; every partition on whole MiB. tools/mkartemis formats and signs an Artemis partition at build with the kernel's own code -- v3's block_subsystem.c (blk_subsys_confirm_format) and artemis_sig.c (artemis_sig_genesis_stamp) -- through a blkio device over a byte range of the image; --check reads one back the same way (read-only). disk_uuid from getrandom(2): each image its own. mkdiskimage.sh gains --starship, --mkartemis, --esp-mib; Milk-V and the Pi images are unchanged. The kernel is not changed. Red: scripts/check_images.sh FAIL (no datastick, the PC image one partition). Green: 23/23. Mutation: --check on a range shifted by one block fails. Accepted: suite EXIT 0; 133146, 133417, 133815, POST 538/538, dict_hash 0xfec8c47cfc6f437f; test-32, 0 failures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a4221d8b8c
commit
e177c5c6b3
Executable
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
# check_images.sh -- build the stick and the data stick and check them
|
||||
# (docs/v4.0.0/plans/2026-10-27-stick-3-images.md, Task 1; docs/v4.0.0/STICK.md 4).
|
||||
#
|
||||
# The stick: make boot_image TARGET=PC -> build/boards/pc/lithos-pc.img
|
||||
# The data stick: make -f kernel/Makefile ARCH=amd64 datastick -> build/amd64/kernel/datastick.img
|
||||
#
|
||||
# Prints PASS:/FAIL: per check and RESULT:; exits 0 only if every check passes.
|
||||
set -uo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
PATH="$PATH:/usr/sbin:/sbin"
|
||||
|
||||
STICK=build/boards/pc/lithos-pc.img
|
||||
DATA=build/amd64/kernel/datastick.img
|
||||
MKARTEMIS=build/amd64/kernel/tools/mkartemis
|
||||
ESP_T=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
|
||||
IDENT_T=23BEBFE1-4817-4CF3-A537-9069BF159788
|
||||
ART_T=BCD7B586-1C8E-4EEF-86A3-273A8D8EE624
|
||||
ok=1
|
||||
|
||||
check() { # check "what" command...
|
||||
local what="$1"; shift
|
||||
if "$@" >/dev/null 2>&1; then echo "PASS: $what"; else echo "FAIL: $what"; ok=0; fi
|
||||
}
|
||||
|
||||
# part IMG N FIELD -- a field (start, size, type) of partition N (1-based) from sfdisk --json
|
||||
part() {
|
||||
sfdisk --json "$1" 2>/dev/null | python3 -c "
|
||||
import json, sys
|
||||
p = json.load(sys.stdin)['partitiontable']
|
||||
parts = p['partitions']
|
||||
n = int(sys.argv[1])
|
||||
if sys.argv[2] == 'count': print(len(parts))
|
||||
elif sys.argv[2] == 'lastlba': print(p['lastlba'])
|
||||
elif n <= len(parts): print(str(parts[n - 1][sys.argv[2]]).upper())
|
||||
" "$2" "$3" 2>/dev/null
|
||||
}
|
||||
|
||||
eq() { [ "$1" = "$2" ]; }
|
||||
|
||||
# geometry IMG -- every partition starts and ends on a whole MiB, the last one within a MiB of
|
||||
# the last usable LBA, and each starts where the one before it ends
|
||||
geometry() {
|
||||
local img="$1" n i prev=2048 s z last
|
||||
n=$(part "$img" 0 count); last=$(part "$img" 0 lastlba)
|
||||
for i in $(seq 1 "$n"); do
|
||||
s=$(part "$img" "$i" start); z=$(part "$img" "$i" size)
|
||||
[ "$s" = "$prev" ] && [ $((s % 2048)) -eq 0 ] && [ $((z % 2048)) -eq 0 ] || return 1
|
||||
prev=$((s + z))
|
||||
done
|
||||
[ $((prev - 1)) -le "$last" ] && [ $((last - (prev - 1))) -lt 2048 ]
|
||||
}
|
||||
|
||||
zeros() { # zeros IMG N -- partition N is all zeros
|
||||
local s z
|
||||
s=$(part "$1" "$2" start); z=$(part "$1" "$2" size)
|
||||
[ -n "$s" ] && [ -n "$z" ] && cmp -s -i "$((s * 512)):0" -n "$((z * 512))" "$1" /dev/zero
|
||||
}
|
||||
|
||||
artemis_check() { # artemis_check IMG N -- mkartemis --check on partition N
|
||||
"$MKARTEMIS" --check "$1" --first-lba "$(part "$1" "$2" start)" --sectors "$(part "$1" "$2" size)"
|
||||
}
|
||||
|
||||
uuid() { artemis_check "$1" "$2" 2>/dev/null | sed -n 's/.*disk_uuid \([0-9a-f]*\).*/\1/p'; }
|
||||
|
||||
echo "== building"
|
||||
make boot_image TARGET=PC >/dev/null 2>&1; echo "boot_image exit $?"
|
||||
make -f kernel/Makefile ARCH=amd64 datastick >/dev/null 2>&1; echo "datastick exit $?"
|
||||
|
||||
echo "== the stick ($STICK)"
|
||||
check "it exists, 2048 MiB" eq "$(stat -c %s "$STICK" 2>/dev/null)" "$((2048 * 1048576))"
|
||||
check "three partitions" eq "$(part "$STICK" 0 count)" 3
|
||||
check "1: EFI System, 64 MiB at LBA 2048" eq "$(part "$STICK" 1 type)/$(part "$STICK" 1 start)/$(part "$STICK" 1 size)" "$ESP_T/2048/131072"
|
||||
check "2: StarshipOS identity, 500 MiB" eq "$(part "$STICK" 2 type)/$(part "$STICK" 2 start)/$(part "$STICK" 2 size)" "$IDENT_T/133120/1024000"
|
||||
check "3: StarshipOS Artemis, the rest" eq "$(part "$STICK" 3 type)/$(part "$STICK" 3 start)" "$ART_T/1157120"
|
||||
check "whole MiB, contiguous, Artemis to the end" geometry "$STICK"
|
||||
check "sfdisk --verify: the GPT is clean" sfdisk --verify "$STICK"
|
||||
OFF=$((2048 * 512))
|
||||
check "the ESP holds EFI/BOOT/BOOTX64.EFI" mdir -i "$STICK@@$OFF" ::/EFI/BOOT/BOOTX64.EFI
|
||||
check "the ESP holds startup.nsh" mdir -i "$STICK@@$OFF" ::/startup.nsh
|
||||
check "starforth.cfg's first word is system=v4" eq "$(mtype -i "$STICK@@$OFF" ::/starforth.cfg 2>/dev/null | awk '{print $1; exit}')" "system=v4"
|
||||
check "the identity partition is all zeros" zeros "$STICK" 2
|
||||
check "Artemis: STFR v2, signature ok" artemis_check "$STICK" 3
|
||||
|
||||
echo "== the data stick ($DATA)"
|
||||
check "it exists, 1536 MiB" eq "$(stat -c %s "$DATA" 2>/dev/null)" "$((1536 * 1048576))"
|
||||
check "two partitions" eq "$(part "$DATA" 0 count)" 2
|
||||
check "1: StarshipOS identity, 500 MiB at LBA 2048" eq "$(part "$DATA" 1 type)/$(part "$DATA" 1 start)/$(part "$DATA" 1 size)" "$IDENT_T/2048/1024000"
|
||||
check "2: StarshipOS Artemis, the rest" eq "$(part "$DATA" 2 type)/$(part "$DATA" 2 start)" "$ART_T/1026048"
|
||||
check "whole MiB, contiguous, Artemis to the end" geometry "$DATA"
|
||||
check "sfdisk --verify: the GPT is clean" sfdisk --verify "$DATA"
|
||||
check "the identity partition is all zeros" zeros "$DATA" 1
|
||||
check "Artemis: STFR v2, signature ok" artemis_check "$DATA" 2
|
||||
U1=$(uuid "$DATA" 2)
|
||||
make -f kernel/Makefile ARCH=amd64 datastick >/dev/null 2>&1
|
||||
U2=$(uuid "$DATA" 2)
|
||||
check "made again, it is fresh: its own disk_uuid ($U1 / $U2)" test -n "$U1" -a -n "$U2" -a "$U1" != "$U2"
|
||||
|
||||
# the builds regenerate these; put them back as committed (as qemu_session.py does)
|
||||
git checkout -q -- v3/include/version.h capsules/BLOCK_MAP.md sbom.spdx sbom.spdx.json
|
||||
|
||||
echo "RESULT: $([ $ok = 1 ] && echo PASS || echo FAIL)"
|
||||
[ $ok = 1 ]
|
||||
+48
-4
@@ -15,6 +15,14 @@
|
||||
# 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.
|
||||
# --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.
|
||||
@@ -23,13 +31,16 @@
|
||||
# root, no loop devices.
|
||||
set -euo pipefail
|
||||
|
||||
OUT="" TABLE="" SIZE_MIB="" LABEL=""
|
||||
OUT="" TABLE="" SIZE_MIB="" LABEL="" IDENT_MIB="" MKARTEMIS="" ESP_MIB=""
|
||||
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 ;;
|
||||
--) shift; break ;;
|
||||
-*) echo "mkdiskimage.sh: unknown option $1" >&2; exit 2 ;;
|
||||
*) break ;;
|
||||
@@ -38,7 +49,12 @@ done
|
||||
|
||||
[ -n "$OUT" ] && [ -n "$TABLE" ] && [ -n "$SIZE_MIB" ] && [ -n "$LABEL" ] || {
|
||||
echo "mkdiskimage.sh: --out, --table, --size-mib and --label are required" >&2; exit 2; }
|
||||
[ $# -gt 0 ] || { echo "mkdiskimage.sh: no SRC:DEST files given" >&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"
|
||||
@@ -54,13 +70,37 @@ case "$TABLE" in
|
||||
*) 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 ))
|
||||
[ "$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 [ "$TABLE" = gpt ]; then
|
||||
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"
|
||||
"$MKARTEMIS" --format "$OUT" --first-lba "$ART_START" --sectors "$ART_SECTORS" >/dev/null || {
|
||||
echo "mkdiskimage.sh: formatting Artemis failed" >&2; rm -f "$OUT"; exit 1; }
|
||||
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
|
||||
@@ -88,4 +128,8 @@ done
|
||||
|
||||
dd if="$ESP" of="$OUT" bs=512 seek="$START" conv=notrunc status=none
|
||||
rm -f "$ESP"
|
||||
echo " IMAGE $OUT ($TABLE, ${SIZE_MIB} MiB, FAT32 '$LABEL')"
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user