summaryrefslogtreecommitdiffstats
path: root/scripts/test-disk.sh
blob: b4b0fde54e70476f6b3e649d0d75d6dcb79db115 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env bash
#
# scripts/test-disk.sh — full automated data-disk round trip.
#
# Builds the kernel, ensures the FAT32 image exists (with --fresh it is
# re-formatted and re-seeded), boots QEMU headless, drives the shell via
# the monitor, then verifies the results with mtools and fsck.
#
# Usage:
#   ./scripts/test-disk.sh            # reuse existing image
#   ./scripts/test-disk.sh --fresh    # re-format the image first
#
# Environment:
#   DISK_IMG  data disk path (default: /tmp/disk.img)
#   OVMF      firmware (default: /usr/share/edk2/x64/OVMF.4m.fd)
#   QEMU      qemu binary (default: qemu-system-x86_64)
#
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
cd "$PROJECT_DIR"

FRESH=0
[ "${1:-}" = "--fresh" ] && FRESH=1

OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}"
QEMU="${QEMU:-qemu-system-x86_64}"
IMG="${SYSTEM_IMG:-/tmp/system.img}"
QPID=/tmp/rsdos-test-qpid
SOCK=/tmp/rsdos-test-qmon.sock
LOG=/tmp/rsdos-test-serial.log
QLOG=/tmp/rsdos-test-qemu.log
STAMP="$(date +%s)"

# Never leave a QEMU behind (an orphan holding our stdout would hang any
# caller that pipes our output).
trap 'kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true' EXIT

fail() {
	echo "FAIL: $1"
	[ -f "$LOG" ] && tail -20 "$LOG"
	exit 1
}

# wait_log FILE PATTERN TIMEOUT-SECONDS — poll a file for a regex.
wait_log() {
	local i=0
	while [ "$i" -lt $((${3:-60} * 10)) ]; do
		grep -q "$2" "$1" 2>/dev/null && return 0
		sleep 0.1
		i=$((i + 1))
	done
	return 1
}

# wait_qemu_exit TIMEOUT-SECONDS — poll until the pid is gone.
wait_qemu_exit() {
	local i=0
	while [ "$i" -lt $((${1:-30} * 10)) ]; do
		kill -0 "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || return 0
		sleep 0.1
		i=$((i + 1))
	done
	return 1
}

echo "==> 1/6 build kernel (debug)"
cargo build --package rsdos --target x86_64-unknown-uefi

echo "==> 2/6 ensure system image"
if [ "$FRESH" -eq 1 ]; then
	"$SCRIPT_DIR/mksystem.sh" --force "$IMG"
else
	"$SCRIPT_DIR/mksystem.sh" "$IMG"
fi

echo "==> 3/6 boot QEMU (headless)"
kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true
sleep 1
rm -f "$SOCK" "$LOG"
# Throwaway OVMF varstore: a stale/poisoned NvVars (e.g. left by the old
# vvfat setup) makes the firmware skip the \EFI\BOOT fallback entirely.
NV="/tmp/rsdos-test-ovmf-vars.fd"
rm -f "$NV"
cp /usr/share/edk2/x64/OVMF.4m.fd "$NV"
"$QEMU" -bios "$NV" \
	-drive file="$IMG",format=raw,if=none,id=disk \
	-device ide-hd,drive=disk,bus=ide.0 \
	-serial file:"$LOG" \
	-monitor unix:"$SOCK",server,nowait \
	-m 256M -machine q35 -cpu qemu64 -no-reboot -display none \
	>"$QLOG" 2>&1 &

wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell"
sleep 1

# Volumes mount under /disk/<UID>/ where UID = "LABEL-SERIAL" (e.g.
# RSDOS-5BD1E55F).  Discover it from the boot log so the test works on
# any image without knowing the serial in advance.
VOL_UID="$(grep -a 'fat: mounted' "$LOG" | sed -n 's/.*at \/disk\/\([^ )]*\).*/\1/p' | head -1)"
[ -n "$VOL_UID" ] || fail "no FAT32 volume was mounted (look for 'fat: mounted' in the log)"
echo "  mounted volume UID: $VOL_UID"

echo "==> 4/6 drive the shell (ls / mkdir / echo WRITING / cat / poweroff)"
# The filename is deliberately >13 chars so the FAT LFN path gets
# multi-entry entries (they're stored last-first on disk).
TESTFILE="disk-test-$STAMP.txt"
python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \
	"ls /disk" \
	"mkdir /disk/$VOL_UID/sub" \
	"echo disk-test-$STAMP WRITING /disk/$VOL_UID/sub/$TESTFILE" \
	"cat /disk/$VOL_UID/sub/$TESTFILE" \
	"poweroff"

echo "==> 5/6 wait for shutdown"
wait_qemu_exit 30 || fail "QEMU did not exit after poweroff"

echo "==> 6/6 verify"
grep -a "disk-test-$STAMP" "$LOG" >/dev/null || fail "cat output missing from serial log"
if ! fsck.fat -n "$IMG" >/tmp/rsdos-fsck.out 2>&1; then
	fail "fsck.fat reports problems: $(cat /tmp/rsdos-fsck.out | tail -3)"
fi
MT="$(mtype -i "$IMG" "::/sub/$TESTFILE" 2>/dev/null || true)"
[ "$MT" = "disk-test-$STAMP" ] || fail "mtype mismatch: got '$MT'"

echo ""
echo "PASS — disk round trip OK (fsck clean, file survived poweroff)"
echo "  image:      $IMG"
echo "  volume:     /disk/$VOL_UID"
echo "  written:    /disk/$VOL_UID/sub/$TESTFILE = \"disk-test-$STAMP\""