diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-01 22:28:34 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-01 22:28:34 +0200 |
| commit | ab7d87bcde31c1681ea09c30534dde840a58d6fa (patch) | |
| tree | 42eed2c86663cf6ecfa3507ca46399e31b441c99 /scripts | |
| parent | rsboot: track latest upstream HEAD (92367a8) instead of pinned ee1c85b (diff) | |
| download | rs-dos-ab7d87bcde31c1681ea09c30534dde840a58d6fa.tar.gz rs-dos-ab7d87bcde31c1681ea09c30534dde840a58d6fa.zip | |
rsnix v0.5.2: fault diagnostics + real-hardware scroll-crash hardening
The real-hardware scroll crash (white bands + instant freeze, QEMU-clean)
was invisible because the only trace was a single serial line on machines
with no serial console. This makes every crash self-identifying and
hardens the real-hardware failure surfaces:
- Every CPU exception vector 0-31 now has an IDT entry (macro-generated
stubs; 8/14 keep their IST stacks). A fault in the shell prints a
white-on-red band to the framebuffer via gop::dump_lines (raw fb
writes bypassing the vt, fully clamped) plus a parseable serial line:
'!!! <kind> vec=N err=… rip=… cr2=… rsp=… rflags=… task=N "name"',
then halts — instead of converging to a cryptic double fault.
Background-task faults abort the task as before.
- Boot log now reports the authoritative post-SetMode geometry:
'gop: fb base=… size=… pitch=… WxH bpp=… bgr=…', so the next real
machine's report identifies whether pitch*height exceeds the mapping.
- Framebuffer writes (clear, push_rect, shift_up_n bulk push, cursor
overlay) are clamped to the firmware's framebuffer_size: a pitch-
padded mode degrades to a truncated view instead of faulting past
the mapping.
- The GOP framebuffer region is excluded from the page allocator (and
subtracted from the heap's conventional-region pick) even when the
firmware lists it as ConventionalMemory — kernel allocations can
never overwrite the display again. The GOP protocol is located and
peeked before the memory map is captured so the exclusion is known
at heap-init time.
- Shell gains a hidden 'fault' command (deliberate unmapped write) and
'make test-fault' verifies the dump: vec=14, cr2=0x400000000000,
task=0, halt — no triple fault. All other suites re-pass.
- CONTEXT.md: fault-dump format documented for real-hardware reports.
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/test-fault.sh | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/scripts/test-fault.sh b/scripts/test-fault.sh new file mode 100755 index 0000000..49ba791 --- /dev/null +++ b/scripts/test-fault.sh | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | # | ||
| 3 | # scripts/test-fault.sh — fault-diagnostics verification. | ||
| 4 | # | ||
| 5 | # Real hardware has no serial console, so a kernel crash previously left | ||
| 6 | # only a frozen screen ("unresponsive"). This test proves the fault-dump | ||
| 7 | # path: every CPU exception now prints a white-on-red band to the | ||
| 8 | # framebuffer *and* a parseable serial line (vector, error, RIP, CR2, | ||
| 9 | # task) before halting — so the next real-hardware failure identifies | ||
| 10 | # itself. The `fault` command deliberately writes to an unmapped | ||
| 11 | # address; the machine must halt with a full dump, not triple-fault. | ||
| 12 | # | ||
| 13 | # Usage: ./scripts/test-fault.sh | ||
| 14 | # Env: OVMF, QEMU (same defaults as other test scripts) | ||
| 15 | # | ||
| 16 | set -euo pipefail | ||
| 17 | |||
| 18 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | ||
| 19 | PROJECT_DIR="$(dirname "$SCRIPT_DIR")" | ||
| 20 | cd "$PROJECT_DIR" | ||
| 21 | |||
| 22 | OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" | ||
| 23 | QEMU="${QEMU:-qemu-system-x86_64}" | ||
| 24 | ESP_DIR="$PROJECT_DIR/esp" | ||
| 25 | QPID=/tmp/rsdos-fault-qpid | ||
| 26 | SOCK=/tmp/rsdos-fault-qmon.sock | ||
| 27 | LOG=/tmp/rsdos-fault-serial.log | ||
| 28 | QLOG=/tmp/rsdos-fault-qemu.log | ||
| 29 | |||
| 30 | trap 'kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true' EXIT | ||
| 31 | |||
| 32 | fail() { | ||
| 33 | echo "FAIL: $1" | ||
| 34 | [ -f "$LOG" ] && tail -40 "$LOG" | ||
| 35 | exit 1 | ||
| 36 | } | ||
| 37 | |||
| 38 | wait_log() { | ||
| 39 | local i=0 | ||
| 40 | while [ "$i" -lt $((${3:-60} * 10)) ]; do | ||
| 41 | grep -q "$2" "$1" 2>/dev/null && return 0 | ||
| 42 | sleep 0.1 | ||
| 43 | i=$((i + 1)) | ||
| 44 | done | ||
| 45 | return 1 | ||
| 46 | } | ||
| 47 | |||
| 48 | echo "==> 1/5 build kernel (debug)" | ||
| 49 | cargo build --package rsdos --target x86_64-unknown-uefi | ||
| 50 | |||
| 51 | echo "==> 2/5 deploy to ESP" | ||
| 52 | mkdir -p "$ESP_DIR/EFI/BOOT" | ||
| 53 | cp target/x86_64-unknown-uefi/debug/rsdos.efi "$ESP_DIR/EFI/BOOT/BOOTX64.EFI" | ||
| 54 | |||
| 55 | echo "==> 3/5 boot QEMU (headless)" | ||
| 56 | kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true | ||
| 57 | sleep 1 | ||
| 58 | rm -f "$SOCK" "$LOG" | ||
| 59 | "$QEMU" -bios "$OVMF" \ | ||
| 60 | -drive file=fat:rw:"$ESP_DIR",format=raw \ | ||
| 61 | -serial file:"$LOG" \ | ||
| 62 | -monitor unix:"$SOCK",server,nowait \ | ||
| 63 | -m 256M -machine q35 -cpu qemu64 -no-reboot -display none \ | ||
| 64 | >"$QLOG" 2>&1 & | ||
| 65 | echo $! >"$QPID" | ||
| 66 | |||
| 67 | wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" | ||
| 68 | |||
| 69 | echo "==> 4/5 boot diagnostics present" | ||
| 70 | grep -aq "gop: fb base=" "$LOG" || fail "fb geometry diagnostic missing" | ||
| 71 | grep -aq "gop: mode .* selected" "$LOG" || fail "mode-selection diagnostic missing" | ||
| 72 | |||
| 73 | echo "==> 5/5 inject a page fault (fault command)" | ||
| 74 | python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ | ||
| 75 | "clear" \ | ||
| 76 | "fault" | ||
| 77 | sleep 2 | ||
| 78 | |||
| 79 | # The dump must appear on serial: vector 14, the CR2 we wrote, RIP, and | ||
| 80 | # the shell task identity — then the machine halts (never triple-faults). | ||
| 81 | grep -aq "!!! page fault vec=14 .*rip=" "$LOG" || fail "page-fault dump line missing" | ||
| 82 | grep -aq "cr2=0x400000000000" "$LOG" || fail "CR2 not reported (faulting address)" | ||
| 83 | grep -aq "task=0 \"shell\"" "$LOG" || fail "faulting task not identified" | ||
| 84 | grep -aq "!!! halting" "$LOG" || fail "no halt message" | ||
| 85 | |||
| 86 | # The screen dump itself is exercised in-kernel (dump_lines); the serial | ||
| 87 | # path sharing the same format line is verified above. | ||
| 88 | echo "" | ||
| 89 | echo "PASS — fault dump works (vec, err, rip, cr2, task → screen + serial)" | ||
| 90 | echo " every CPU exception now halts with a diagnosable dump, not a freeze" | ||
