seed-accept.sh (10218B)
1 #!/bin/sh 2 ## tests/seed-accept.sh — seed-driver acceptance tests. 3 ## 4 ## One script, three modes: 5 ## 6 ## kernel (default) Tier-2 acceptance for boot0/1/2 on seed-kernel: 7 ## loads boot2-built scheme1 as /init, runs a 8 ## driver.scm that exercises read/write/openat/ 9 ## close/brk/exit_group + clone/execve/waitid via 10 ## a child catm spawn. Verifies transcript markers 11 ## and that the extracted output file is correct. 12 ## 13 ## boot34 Run boot3 (and optionally boot4 with WITH_BOOT4=1) 14 ## under DRIVER=seed and assert byte-identical 15 ## outputs vs the podman reference. 16 ## 17 ## boot5 Run boot5 under DRIVER=seed and assert byte- 18 ## identical libc.a / crt1.o / crti.o / crtn.o / 19 ## hello vs the podman reference. 20 ## 21 ## All three modes target ARCH=aarch64 (the only seed-driver-complete 22 ## arch today). Prereq for every mode: build/aarch64/podman/boot{0..6}/ 23 ## populated via `./boot/boot.sh aarch64` (default DRIVER=podman), 24 ## including boot6/Image as the seed kernel. 25 ## 26 ## Usage: 27 ## tests/seed-accept.sh # mode=kernel 28 ## tests/seed-accept.sh kernel 29 ## tests/seed-accept.sh boot34 # also: WITH_BOOT4=1 … 30 ## tests/seed-accept.sh boot5 31 32 set -eu 33 34 MODE=${1:-kernel} 35 case "$MODE" in 36 kernel|boot34|boot5) ;; 37 *) echo "$0: unknown mode '$MODE' (kernel | boot34 | boot5)" >&2; exit 2 ;; 38 esac 39 40 ARCH=aarch64 41 ROOT=$(cd "$(dirname "$0")/.." && pwd) 42 cd "$ROOT" 43 44 PODMAN=build/$ARCH/podman 45 SEED=build/$ARCH/seed 46 KERNEL=$PODMAN/boot6/Image 47 48 [ -f "$KERNEL" ] || { 49 echo "missing $KERNEL — run ./boot/boot.sh $ARCH (default DRIVER=podman) first" >&2 50 exit 1 51 } 52 53 # ─── Mode: boot34 ───────────────────────────────────────────────────── 54 if [ "$MODE" = "boot34" ]; then 55 [ -x $PODMAN/boot3/tcc0 ] || { 56 echo "$PODMAN/boot3/tcc0 missing — run boot/boot3.sh aarch64" >&2 57 exit 1 58 } 59 60 echo "[seed-accept boot34] DRIVER=seed boot/boot3.sh $ARCH" 61 DRIVER=seed boot/boot3.sh $ARCH 62 63 if ! cmp -s $SEED/boot3/tcc0 $PODMAN/boot3/tcc0; then 64 s_seed=$(wc -c < $SEED/boot3/tcc0) 65 s_ref=$(wc -c < $PODMAN/boot3/tcc0) 66 echo "[seed-accept boot34] boot3 FAIL: tcc0 differs (seed=$s_seed podman=$s_ref)" >&2 67 exit 3 68 fi 69 echo "[seed-accept boot34] boot3 PASS — tcc0 byte-identical vs podman" 70 71 if [ "${WITH_BOOT4:-0}" != 1 ]; then 72 exit 0 73 fi 74 75 [ -x $PODMAN/boot4/tcc3 ] || { 76 echo "$PODMAN/boot4/tcc3 missing — run boot/boot4.sh aarch64 under podman first" >&2 77 exit 1 78 } 79 80 echo "[seed-accept boot34] DRIVER=seed boot/boot4.sh $ARCH" 81 DRIVER=seed boot/boot4.sh $ARCH 82 83 fail=0 84 # All boot4 outputs — including the intermediate crt1.o / libc.a / 85 # libtcc1.a — must match podman byte-for-byte. The strip-file-prefix 86 # tcc patch (simple-patches/tcc-0.9.26/) drops the /work/in/[tcc-lib/] 87 # mount prefix from STT_FILE entries, so seed's flat-basename staging 88 # and podman's /work/in/ mounts produce identical .o relocations. 89 for f in tcc3 hello crt1.o libc.a libtcc1.a; do 90 if ! cmp -s $SEED/boot4/$f $PODMAN/boot4/$f; then 91 s_seed=$(wc -c < $SEED/boot4/$f) 92 s_ref=$(wc -c < $PODMAN/boot4/$f) 93 echo "[seed-accept boot34] boot4 DIFF $f: seed=$s_seed podman=$s_ref" >&2 94 fail=1 95 fi 96 done 97 [ $fail -eq 0 ] || exit 4 98 echo "[seed-accept boot34] boot4 PASS — tcc3/hello/crt1.o/libc.a/libtcc1.a byte-identical vs podman" 99 exit 0 100 fi 101 102 # ─── Mode: boot5 ────────────────────────────────────────────────────── 103 if [ "$MODE" = "boot5" ]; then 104 [ -d $PODMAN/boot5 ] || { 105 echo "$PODMAN/boot5 missing — run boot/boot5.sh aarch64" >&2 106 exit 1 107 } 108 for f in libc.a crt1.o crti.o crtn.o hello; do 109 [ -e $PODMAN/boot5/$f ] || { 110 echo "$PODMAN/boot5/$f missing — run boot/boot5.sh aarch64" >&2 111 exit 1 112 } 113 done 114 115 echo "[seed-accept boot5] DRIVER=seed boot/boot5.sh $ARCH" 116 DRIVER=seed boot/boot5.sh $ARCH 117 118 fails=0 119 for f in libc.a crt1.o crti.o crtn.o hello; do 120 seed_size=$(wc -c < $SEED/boot5/$f) 121 ref_size=$(wc -c < $PODMAN/boot5/$f) 122 if cmp -s $SEED/boot5/$f $PODMAN/boot5/$f; then 123 echo "[seed-accept boot5] $f: byte-identical ($seed_size bytes)" 124 else 125 echo "[seed-accept boot5] $f: DIFF (seed=$seed_size podman=$ref_size)" 126 fails=$((fails + 1)) 127 fi 128 done 129 130 if [ $fails -eq 0 ]; then 131 echo "[seed-accept boot5] PASS — all 5 outputs byte-identical" 132 exit 0 133 else 134 echo "[seed-accept boot5] FAIL — $fails outputs differ" >&2 135 exit 4 136 fi 137 fi 138 139 # ─── Mode: kernel ───────────────────────────────────────────────────── 140 141 EXTRACT=seed-kernel/scripts/extract-blk.sh 142 SCHEME1=$PODMAN/boot2/scheme1 143 CATM=$PODMAN/boot2/catm 144 PRELUDE=scheme1/prelude.scm 145 146 [ -x "$SCHEME1" ] || { echo "missing $SCHEME1 — run boot2 first" >&2; exit 1; } 147 [ -x "$CATM" ] || { echo "missing $CATM — run boot2 first" >&2; exit 1; } 148 149 OUTDIR=$ROOT/build/$ARCH/seed-accept 150 rm -rf "$OUTDIR"; mkdir -p "$OUTDIR" 151 152 STAGE=$(mktemp -d -t seed-accept.XXXXXX) 153 trap 'rm -rf "$STAGE"' EXIT 154 155 # ─── driver.scm — the in-VM acceptance program ──────────────────────── 156 cat > "$STAGE/driver.scm" <<'SCM' 157 ;; driver.scm — Tier-2 acceptance for seed-kernel. 158 (write-string stdout "scheme1: hello from acceptance driver\n") 159 (write-string stdout "scheme1: spawning child-prog (catm) C <- A + B\n") 160 161 (let ((r (run "child-prog" "C" "A" "B"))) 162 (if (car r) 163 (begin 164 (write-string stdout "scheme1: child returned\n")) 165 (begin 166 (write-string stdout "scheme1: spawn FAILED\n") 167 (exit 1)))) 168 169 (let ((rp (open-input "C"))) 170 (if (car rp) 171 (let* ((p (cdr rp)) 172 (rb (read-all p))) 173 (if (car rb) 174 (begin 175 (write-string stdout "scheme1: read C: [") 176 (write-bytes stdout (cdr rb)) 177 (write-string stdout "]\n")) 178 (write-string stdout "scheme1: read C FAILED\n")) 179 (close p)) 180 (write-string stdout "scheme1: open C FAILED\n"))) 181 182 (write-string stdout "scheme1: ALL-OK\n") 183 (exit 0) 184 SCM 185 186 # ─── Combine prelude + driver via host catm — this matches the chain's 187 # own boot-run-scheme1.sh wrapper, so the .scm shape is identical 188 # to what scheme1 expects everywhere. ────────────────────────────── 189 cat "$PRELUDE" "$STAGE/driver.scm" > "$STAGE/combined.scm" 190 191 # ─── Two demo input files. catm reads them from the in-VM tmpfs. ────── 192 printf 'Hello, ' > "$STAGE/A" 193 printf 'seed-kernel!\n' > "$STAGE/B" 194 195 # ─── Stage the cpio: /init=scheme1, /child-prog=catm, plus inputs. ──── 196 mkdir -p "$STAGE/cpio" 197 cp "$SCHEME1" "$STAGE/cpio/init"; chmod +x "$STAGE/cpio/init" 198 cp "$CATM" "$STAGE/cpio/child-prog"; chmod +x "$STAGE/cpio/child-prog" 199 cp "$STAGE/combined.scm" "$STAGE/cpio/combined.scm" 200 cp "$STAGE/A" "$STAGE/cpio/A" 201 cp "$STAGE/B" "$STAGE/cpio/B" 202 203 NAMES='init 204 child-prog 205 combined.scm 206 A 207 B' 208 209 ( cd "$STAGE/cpio" && printf '%s\n' "$NAMES" | cpio -o -H newc 2>/dev/null ) > "$STAGE/initramfs.cpio" 210 sz=$(wc -c < "$STAGE/initramfs.cpio") 211 pad=$(( (512 - sz % 512) % 512 )) 212 if [ "$pad" -gt 0 ]; then 213 head -c "$pad" /dev/zero >> "$STAGE/initramfs.cpio" 214 fi 215 mv "$STAGE/initramfs.cpio" "$STAGE/in.img" 216 truncate -s 256M "$STAGE/out.img" 217 218 TRANSCRIPT=$OUTDIR/transcript.txt 219 echo "[seed-accept] booting scheme1 + driver.scm on seed-kernel" 220 qemu-system-aarch64 \ 221 -machine virt,gic-version=3,accel=hvf -cpu host -m 2048M \ 222 -nographic -no-reboot \ 223 -global virtio-mmio.force-legacy=false \ 224 -kernel "$KERNEL" \ 225 -drive file="$STAGE/in.img",if=none,format=raw,id=hd0,readonly=on \ 226 -device virtio-blk-device,drive=hd0 \ 227 -drive file="$STAGE/out.img",if=none,format=raw,id=hd1 \ 228 -device virtio-blk-device,drive=hd1 \ 229 -append "init combined.scm" \ 230 > "$TRANSCRIPT" 2>&1 & 231 QPID=$! 232 ( sleep 240; kill -9 $QPID 2>/dev/null ) </dev/null >/dev/null 2>&1 & 233 WATCHER=$! 234 wait $QPID 2>/dev/null || true 235 kill $WATCHER 2>/dev/null || true 236 237 # Extract files (we want C from the tmpfs). 238 if ! "$EXTRACT" "$OUTDIR" "$STAGE/out.img" >/dev/null 2>&1; then 239 echo "[seed-accept] FAIL: extract-blk failed (kernel didn't reach exit?)" >&2 240 tail -60 "$TRANSCRIPT" >&2 241 exit 3 242 fi 243 244 # ─── Verify ─────────────────────────────────────────────────────────── 245 fail=0 246 for needle in \ 247 'scheme1: hello from acceptance driver' \ 248 'scheme1: spawning child-prog' \ 249 'scheme1: child returned' \ 250 'scheme1: read C: \[Hello, seed-kernel!' \ 251 'scheme1: ALL-OK' \ 252 'exit_group(0)' 253 do 254 if ! grep -q "$needle" "$TRANSCRIPT"; then 255 echo "[seed-accept] MISSING in transcript: $needle" >&2 256 fail=1 257 fi 258 done 259 260 if [ ! -f "$OUTDIR/C" ]; then 261 echo "[seed-accept] MISSING extracted file: $OUTDIR/C" >&2 262 fail=1 263 elif ! diff -q "$OUTDIR/C" - <<EOF >/dev/null 264 Hello, seed-kernel! 265 EOF 266 then 267 echo "[seed-accept] C differs from expected:" >&2 268 od -c "$OUTDIR/C" | head -3 >&2 269 fail=1 270 fi 271 272 if [ $fail -ne 0 ]; then 273 echo "[seed-accept] FAIL — see $TRANSCRIPT" >&2 274 exit 4 275 fi 276 277 echo "" 278 echo "=== driver log (excerpt from transcript) ===" 279 grep '^scheme1:' "$TRANSCRIPT" || true 280 echo "===========================================" 281 echo "" 282 echo "[seed-accept] PASS — scheme1 + .scm + child-prog cycle complete" 283 echo "[seed-accept] artifacts in $OUTDIR/"