test(build): Zuse's minted pair as a stick -- the acceptance boots and the sessions use it
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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b2278b6204
commit
38b2b9693a
@@ -4,6 +4,9 @@
|
||||
#
|
||||
# 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
|
||||
# Zuse's pair as a stick (docs/v4.0.0/plans/2026-10-27-stick-4-one-disk.md, Task 1):
|
||||
# make -f kernel/Makefile ARCH=amd64 zusestick -> build/amd64/kernel/zusestick.img
|
||||
# make boot_image TARGET=PC STICK_PAIR=disk/zuse-scratch -> the PC stick with the pair in it
|
||||
#
|
||||
# Prints PASS:/FAIL: per check and RESULT:; exits 0 only if every check passes.
|
||||
set -uo pipefail
|
||||
@@ -12,6 +15,8 @@ PATH="$PATH:/usr/sbin:/sbin"
|
||||
|
||||
STICK=build/boards/pc/lithos-pc.img
|
||||
DATA=build/amd64/kernel/datastick.img
|
||||
ZSTICK=build/amd64/kernel/zusestick.img
|
||||
PAIR=disk/zuse-scratch
|
||||
MKARTEMIS=build/amd64/kernel/tools/mkartemis
|
||||
ESP_T=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
|
||||
IDENT_T=23BEBFE1-4817-4CF3-A537-9069BF159788
|
||||
@@ -61,6 +66,13 @@ 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)"
|
||||
}
|
||||
|
||||
same() { # same IMG N FILE -- partition N holds FILE's bytes, and is exactly its size
|
||||
local s z
|
||||
s=$(part "$1" "$2" start); z=$(part "$1" "$2" size)
|
||||
[ -n "$s" ] && [ -n "$z" ] && [ $((z * 512)) -eq "$(stat -c %s "$3")" ] &&
|
||||
cmp -s -i "$((s * 512)):0" -n "$((z * 512))" "$1" "$3"
|
||||
}
|
||||
|
||||
uuid() { artemis_check "$1" "$2" 2>/dev/null | sed -n 's/.*disk_uuid \([0-9a-f]*\).*/\1/p'; }
|
||||
|
||||
echo "== building"
|
||||
@@ -98,6 +110,32 @@ check "the data stick builds again" make -f kernel/Makefile ARCH=amd64 datastick
|
||||
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"
|
||||
|
||||
echo "== Zuse's pair as a stick ($ZSTICK)"
|
||||
rm -f "$ZSTICK"
|
||||
check "it builds (make zusestick)" make -f kernel/Makefile ARCH=amd64 zusestick
|
||||
check "it exists, 1090 MiB (1 + 64 + 1024 + 1)" eq "$(stat -c %s "$ZSTICK" 2>/dev/null)" "$((1090 * 1048576))"
|
||||
check "two partitions" eq "$(part "$ZSTICK" 0 count)" 2
|
||||
check "1: StarshipOS identity, 64 MiB at LBA 2048" eq "$(part "$ZSTICK" 1 type)/$(part "$ZSTICK" 1 start)/$(part "$ZSTICK" 1 size)" "$IDENT_T/2048/131072"
|
||||
check "2: StarshipOS Artemis, 1 GiB after it" eq "$(part "$ZSTICK" 2 type)/$(part "$ZSTICK" 2 start)/$(part "$ZSTICK" 2 size)" "$ART_T/133120/2097152"
|
||||
check "sfdisk --verify: the GPT is clean" sfdisk --verify "$ZSTICK"
|
||||
check "the identity partition is $PAIR/zuse-thumb-ident.img" same "$ZSTICK" 1 "$PAIR/zuse-thumb-ident.img"
|
||||
check "the Artemis partition is $PAIR/artemis.img" same "$ZSTICK" 2 "$PAIR/artemis.img"
|
||||
check "Artemis: STFR v2, signature ok" artemis_check "$ZSTICK" 2
|
||||
|
||||
echo "== the PC stick with the pair in it (STICK_PAIR=$PAIR)"
|
||||
rm -f "$STICK"
|
||||
check "it builds (make boot_image TARGET=PC STICK_PAIR=$PAIR)" make boot_image TARGET=PC STICK_PAIR=$PAIR
|
||||
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, 64 MiB" eq "$(part "$STICK" 2 type)/$(part "$STICK" 2 start)/$(part "$STICK" 2 size)" "$IDENT_T/133120/131072"
|
||||
check "3: StarshipOS Artemis, 1 GiB" eq "$(part "$STICK" 3 type)/$(part "$STICK" 3 start)/$(part "$STICK" 3 size)" "$ART_T/264192/2097152"
|
||||
check "sfdisk --verify: the GPT is clean" sfdisk --verify "$STICK"
|
||||
check "the ESP holds EFI/BOOT/BOOTX64.EFI" mdir -i "$STICK@@$OFF" ::/EFI/BOOT/BOOTX64.EFI
|
||||
check "the identity partition is the pair's" same "$STICK" 2 "$PAIR/zuse-thumb-ident.img"
|
||||
check "the Artemis partition is the pair's" same "$STICK" 3 "$PAIR/artemis.img"
|
||||
check "Artemis: STFR v2, signature ok" artemis_check "$STICK" 3
|
||||
check "a pair file that is not a whole MiB is refused" eval '{ bash scripts/mkdiskimage.sh --out build/amd64/kernel/tools/check-images/odd.img --table gpt --label X --esp-mib 0 --starship-from scripts/check_images.sh "$PAIR/artemis.img" 2>&1 || true; } | grep -q "not a whole MiB"'
|
||||
|
||||
echo "== mkartemis refuses what it cannot do safely"
|
||||
T=build/amd64/kernel/tools/check-images; mkdir -p "$T"
|
||||
fresh() { rm -f "$T/t.img"; truncate -s 64M "$T/t.img"; }
|
||||
|
||||
+29
-3
@@ -23,6 +23,13 @@
|
||||
# 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.
|
||||
@@ -31,7 +38,7 @@
|
||||
# root, no loop devices.
|
||||
set -euo pipefail
|
||||
|
||||
OUT="" TABLE="" SIZE_MIB="" LABEL="" IDENT_MIB="" MKARTEMIS="" ESP_MIB=""
|
||||
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 ;;
|
||||
@@ -41,12 +48,25 @@ while [ $# -gt 0 ]; do
|
||||
--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
|
||||
@@ -79,6 +99,7 @@ if [ -n "$IDENT_MIB" ]; then
|
||||
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
|
||||
|
||||
@@ -94,8 +115,13 @@ if [ -n "$IDENT_MIB" ]; then
|
||||
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 [ -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
|
||||
|
||||
+11
-4
@@ -5,7 +5,8 @@
|
||||
|
||||
Builds and boots `make ARCH=<arch> clean qemu` (one QEMU, in the foreground of
|
||||
this script), with copies of the minted Zuse pair (disk/zuse-scratch/) made
|
||||
under build/qemu-session/<label>/ so the committed images are never booted.
|
||||
under build/qemu-session/<label>/ so the committed images are never booted;
|
||||
Zuse's drive is that pair laid out as a stick (zusestick.img).
|
||||
It reads the serial socket continuously, drives the boot over the serial and
|
||||
QMP sockets the `qemu` goal opens, prints PASS:/FAIL: lines and
|
||||
RESULT: PASS|FAIL, and quits QEMU over QMP. The serial log is the usual
|
||||
@@ -78,7 +79,13 @@ os.makedirs(work)
|
||||
for f in ("artemis.img", "zuse-thumb-ident.img"):
|
||||
subprocess.run(["cp", "--sparse=always", os.path.join(ROOT, "disk/zuse-scratch", f), work], check=True)
|
||||
art = os.path.join(work, "artemis.img")
|
||||
zimg = os.path.join(work, "zuse-thumb-ident.img")
|
||||
ident = os.path.join(work, "zuse-thumb-ident.img") # the raw identity image (GPT-mode payloads)
|
||||
# Zuse's drive is her pair as a stick (plan 2026-10-27-stick-4-one-disk.md, Task 1): identity and
|
||||
# Artemis partitions on one disk, built from this session's copies
|
||||
zimg = os.path.join(work, "zusestick.img")
|
||||
subprocess.run(["bash", os.path.join(ROOT, "scripts/mkdiskimage.sh"), "--out", zimg, "--table", "gpt",
|
||||
"--label", "ZUSESTICK", "--esp-mib", "0", "--starship-from", ident, art],
|
||||
check=True, stdout=subprocess.DEVNULL)
|
||||
attach_zuse = not (a.no_zuse or a.mode in NO_ZUSE_MODES)
|
||||
|
||||
make_args = ["make", "-f", "kernel/Makefile", f"ARCH={a.arch}", "clean", a.target, f"ARTDISK={art}",
|
||||
@@ -491,13 +498,13 @@ elif m == "emergency_pull":
|
||||
elif m in ("gpt_zuse", "gpt_hotplug", "gpt_foreign", "gpt_bad_crc", "gpt_bad_entries", "gpt_no_header",
|
||||
"gpt_read_error"):
|
||||
gimg = os.path.join(work, "gpt.img")
|
||||
make_gpt_image(gimg, GPT_LINUX if m == "gpt_foreign" else GPT_IDENTITY, zimg,
|
||||
make_gpt_image(gimg, GPT_LINUX if m == "gpt_foreign" else GPT_IDENTITY, ident,
|
||||
break_crc=(m == "gpt_bad_crc"), break_entries=(m == "gpt_bad_entries"),
|
||||
break_sig=(m == "gpt_no_header"))
|
||||
check("no ok> for 10 s with no drive", not wait_for(ANYPROMPT, 10))
|
||||
b0 = blk_lines()
|
||||
if m == "gpt_read_error":
|
||||
add_zuse(1, zimg, fail_sector0=True) # the raw Zuse drive: no GPT, but block 0 unreadable
|
||||
add_zuse(1, ident, fail_sector0=True) # the raw Zuse drive: no GPT, but block 0 unreadable
|
||||
else:
|
||||
add_zuse(1, gimg)
|
||||
if m in ("gpt_zuse", "gpt_hotplug"):
|
||||
|
||||
Reference in New Issue
Block a user