diff --git a/.github/workflows/nightly-harnesses.yml b/.github/workflows/nightly-harnesses.yml index db6e527..3ff4fce 100644 --- a/.github/workflows/nightly-harnesses.yml +++ b/.github/workflows/nightly-harnesses.yml @@ -98,6 +98,20 @@ jobs: make -j"$(nproc)" kernel.elf file kernel.elf + # 51 harnesses copy disk.img to a scratch path and exit 2 without it. It + # is an untracked, gitignored 128 MB binary that had only ever been made + # by hand on one machine, so it simply did not exist on a runner: the + # first full nightly run lost most of the suite to "disk.img not found", + # which reads as a wall of kernel failures rather than a missing fixture. + # + # The fixture it copies in (userspace/info.elf.signed -- the 14144 bytes + # verify-fsyscalls.sh asserts on) is TRACKED, so this needs nothing from + # the build and the ordering here is not load-bearing. + - name: Build the FAT32 disk.img that the harnesses mount as drive C + run: | + bash tools/make-disk-img.sh + ls -l disk.img + - name: Run the suite run: | bash verify/run-all.sh diff --git a/tools/make-disk-img.sh b/tools/make-disk-img.sh new file mode 100755 index 0000000..75b0c71 --- /dev/null +++ b/tools/make-disk-img.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +#============================================================================== +# make-disk-img.sh -- build the FAT32 `disk.img` the harness suite needs for C:. +# +# WHY THIS EXISTS +# +# 51 of the harnesses in verify/ reference disk.img, copying it to a scratch +# path and exiting 2 if it is missing. Nothing in the repo built it: it was an +# untracked, gitignored artifact that existed only on the machine where it had +# once been made by hand. That is invisible locally -- the file is just there +# -- and fatal anywhere else. The first full nightly run lost most of the suite +# to "disk.img not found", which reads as a wall of kernel failures. +# +# It is NOT committed to git: a 128 MB binary in a repo, ~99.99% of it zeroes, +# to hold one 14 KB file that is already tracked. Generating it takes under a +# second; storing it does not. +# +# WHAT THE HARNESSES ACTUALLY REQUIRE +# +# 1. A REAL FAT32 volume. verify-ring3-fatls.sh checks the FAT32 signature at +# offset 0x52 and refuses a zeroed image, because against one C: would not +# mount and `ls C:/` would fail for a reason unrelated to what it tests -- +# a vacuous run that scores as a kernel bug. +# +# 2. C:/HELLO.ELF, 14144 bytes. userspace/fileio.c opens it O_RDONLY and +# lseeks to SEEK_END; verify-fsyscalls.sh asserts the result is EXACTLY +# 14144, so the size is load-bearing. It is never executed -- only read -- +# so this is a size-and-content fixture, not a runnable binary. +# +# We use info.elf.signed, which is 14144 bytes. Do NOT substitute +# hello.elf.signed just because the names match: it is 14388 bytes and +# would fail that assertion. (The hand-made image had a stale 14144-byte +# HELLO.ELF from an older signing run, which is why the name never +# matched its content.) +# +# Idempotent: rebuilds from scratch every time, so a corrupted image is fixed +# by re-running rather than debugged. +#============================================================================== +set -euo pipefail +cd "$(dirname "$0")/.." + +OUT="${1:-disk.img}" +FIXTURE="userspace/info.elf.signed" +FIXTURE_SIZE=14144 + +command -v mformat >/dev/null 2>&1 || { + echo "ERROR: mformat not found (install mtools: brew install mtools / apt-get install mtools)" >&2 + exit 1 +} + +# The fixture is tracked in git, so a checkout has it. If it is missing, say so +# rather than silently producing an image without the one file C: must contain. +if [ ! -f "$FIXTURE" ]; then + echo "ERROR: $FIXTURE not found (it is tracked -- is this a full checkout?)" >&2 + exit 1 +fi + +# Assert the size the harness hardcodes. If signing ever changes it, fail HERE +# with the real reason rather than 800 s later inside verify-fsyscalls.sh as a +# confusing "SEEK_END reported N, expected 14144". +actual=$(wc -c < "$FIXTURE" | tr -d ' ') +if [ "$actual" -ne "$FIXTURE_SIZE" ]; then + echo "ERROR: $FIXTURE is $actual bytes, but verify-fsyscalls.sh asserts" >&2 + echo " C:/HELLO.ELF is exactly $FIXTURE_SIZE. Update both together." >&2 + exit 1 +fi + +echo "==> Creating 128 MB FAT32 image: $OUT" +rm -f "$OUT" +# bs=1M, not 1m: GNU dd rejects the lowercase suffix outright, and BSD dd +# accepts both. The suite used 1m and died on every Linux runner. +dd if=/dev/zero of="$OUT" bs=1M count=128 status=none + +# Geometry matches the original hand-made image: 63 sectors/track, 16 heads, +# 2 sectors/cluster, 32 reserved. -F forces FAT32 (mformat would otherwise +# pick FAT16 at this size, and the driver under test is the FAT32 one). +mformat -i "$OUT" -F -T 262144 -h 16 -s 63 -c 2 -R 32 :: + +mcopy -i "$OUT" "$FIXTURE" ::HELLO.ELF + +# INFO.ELF was also present in the hand-made image. No harness or userspace +# source opens it -- the only two mentions in the tree are a usage example in +# qemu_typist.py and a comment in verify-exec-frame-leak.sh warning NOT to use +# it (it is a larger image and would move that harness's expected page count). +# Included anyway so the generated image is a faithful replacement rather than +# a subset, which keeps "it worked before" from meaning something different. +mcopy -i "$OUT" "$FIXTURE" ::INFO.ELF + +# Verify what we produced rather than trusting mformat's exit code: check the +# same FAT32 signature verify-ring3-fatls.sh checks, and read the file back. +if ! dd if="$OUT" bs=1 skip=82 count=8 status=none | grep -q "FAT32"; then + echo "ERROR: produced image has no FAT32 signature at 0x52" >&2 + exit 1 +fi +back=$(mdir -i "$OUT" ::HELLO.ELF 2>/dev/null | grep -c "HELLO" || true) +[ "$back" -ge 1 ] || { echo "ERROR: HELLO.ELF not readable back from $OUT" >&2; exit 1; } + +echo "==> OK: $OUT ($(wc -c < "$OUT" | tr -d ' ') bytes), C:/HELLO.ELF = $FIXTURE_SIZE bytes" diff --git a/verify/auto-verify-exec.sh b/verify/auto-verify-exec.sh index 63af7cd..8617e61 100755 --- a/verify/auto-verify-exec.sh +++ b/verify/auto-verify-exec.sh @@ -32,7 +32,7 @@ i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" # 128 MiB zeroed image, same geometry as disk.img -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU (monitor on $MON_SOCK)" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-argv.sh b/verify/verify-argv.sh index d3b1deb..fb00516 100755 --- a/verify/verify-argv.sh +++ b/verify/verify-argv.sh @@ -46,7 +46,7 @@ i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU (monitor on $MON_SOCK)" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-bgjobs.sh b/verify/verify-bgjobs.sh index 0e3b4da..11abafc 100755 --- a/verify/verify-bgjobs.sh +++ b/verify/verify-bgjobs.sh @@ -35,7 +35,7 @@ i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU (monitor on $MON_SOCK)" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-chmod-owner.sh b/verify/verify-chmod-owner.sh index 6296117..17feaa2 100755 --- a/verify/verify-chmod-owner.sh +++ b/verify/verify-chmod-owner.sh @@ -53,7 +53,7 @@ cp kernel.elf iso/boot/kernel.elf i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 || { echo "FAIL: ISO"; exit 2; } rm -f "$RUN_DISK" "$SERIAL" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ -boot d -m 256M \ diff --git a/verify/verify-ids-spray.sh b/verify/verify-ids-spray.sh index 23a4cef..87d8586 100755 --- a/verify/verify-ids-spray.sh +++ b/verify/verify-ids-spray.sh @@ -120,7 +120,7 @@ run_case() { rm -f "$serial" mon=$(mktemp -u /tmp/tinyos-spray-mon.XXXXXX) disk=$(mktemp -u /tmp/tinyos-spray-disk.XXXXXX) - dd if=/dev/zero of="$disk" bs=1m count=16 >/dev/null 2>&1 + dd if=/dev/zero of="$disk" bs=1M count=16 >/dev/null 2>&1 qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ -boot d -m 256M \ diff --git a/verify/verify-pipes-exec.sh b/verify/verify-pipes-exec.sh index 3e72167..4d6d848 100755 --- a/verify/verify-pipes-exec.sh +++ b/verify/verify-pipes-exec.sh @@ -67,7 +67,7 @@ i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU (monitor on $MON_SOCK)" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-pipes.sh b/verify/verify-pipes.sh index b1aff78..3161528 100755 --- a/verify/verify-pipes.sh +++ b/verify/verify-pipes.sh @@ -46,7 +46,7 @@ i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU (monitor on $MON_SOCK)" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-redirect.sh b/verify/verify-redirect.sh index e1c21dc..eb53fd0 100755 --- a/verify/verify-redirect.sh +++ b/verify/verify-redirect.sh @@ -45,7 +45,7 @@ i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU (monitor on $MON_SOCK)" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-ring3-fileops.sh b/verify/verify-ring3-fileops.sh index cbb2c59..b0e5274 100755 --- a/verify/verify-ring3-fileops.sh +++ b/verify/verify-ring3-fileops.sh @@ -110,7 +110,7 @@ i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 || { echo "FAIL: ISO"; exit echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-sectest-redirect.sh b/verify/verify-sectest-redirect.sh index 86eebf5..a78b3eb 100755 --- a/verify/verify-sectest-redirect.sh +++ b/verify/verify-sectest-redirect.sh @@ -132,7 +132,7 @@ echo "==> Guard: scheduler_stats() prints only outside its critical section" echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU (monitor on $MON_SOCK)" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-shell-history-logout.sh b/verify/verify-shell-history-logout.sh index 5815549..8b73836 100755 --- a/verify/verify-shell-history-logout.sh +++ b/verify/verify-shell-history-logout.sh @@ -70,7 +70,7 @@ cp kernel.elf iso/boot/kernel.elf i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 || guard_fail "mkrescue failed" rm -f "$RUN_DISK" "$SERIAL" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU..." qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-spawn.sh b/verify/verify-spawn.sh index c9c668f..f3df185 100755 --- a/verify/verify-spawn.sh +++ b/verify/verify-spawn.sh @@ -47,7 +47,7 @@ i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU (monitor on $MON_SOCK)" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-syscall-reject-counters.sh b/verify/verify-syscall-reject-counters.sh index 03b8b0e..9f3c12e 100755 --- a/verify/verify-syscall-reject-counters.sh +++ b/verify/verify-syscall-reject-counters.sh @@ -85,7 +85,7 @@ cp kernel.elf iso/boot/kernel.elf i686-elf-grub-mkrescue -o "$ISO" iso >/dev/null 2>&1 || guard_fail "mkrescue failed" rm -f "$RUN_DISK" "$SERIAL" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU..." qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \ diff --git a/verify/verify-sysredirect.sh b/verify/verify-sysredirect.sh index eba1fe3..b17bafe 100755 --- a/verify/verify-sysredirect.sh +++ b/verify/verify-sysredirect.sh @@ -105,7 +105,7 @@ echo "==> Guard: cmd_mem makes $mem_calls stream_printf call(s) in kernel.elf" echo "==> Fresh BLANK disk (forces first-boot password setup)" rm -f "$RUN_DISK" "$SERIAL" "$TRACE" "$MON_SOCK" -dd if=/dev/zero of="$RUN_DISK" bs=1m count=128 status=none +dd if=/dev/zero of="$RUN_DISK" bs=1M count=128 status=none echo "==> Launching headless QEMU (monitor on $MON_SOCK)" qemu-system-i386 -cpu Broadwell,+rdrand,+rdseed -cdrom "$ISO" \