From acd0fe1bc38746f36fff24a7497041e5c5010977 Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 27 Aug 2026 18:35:59 +0200 Subject: scripts: migrate all QEMU/test scripts to single system image boot.sh: replace esp/ + DISK_IMG with single SYSTEM_IMG on ide.0 (hooks into mksystem.sh --force for auto-staging). boot-image.sh: delegate to mksystem.sh --chain (build-image.sh deleted). build-image-release.sh: delegate to mksystem.sh --chain --gpt. qemu.sh: flexible launcher uses SYSTEM_IMG; --chain flag for full-chain build. test-disk.sh: drop ESP_DIR (mksystem.sh stages everything). test-fault.sh: boot system.img on ide.0 + CRASHLOG stick on ide.1; replace deleted mkdisk.sh with inline mkfs.fat. test-gfx.sh: system.img on ide.0, stage via mksystem.sh. test-mouse.sh: system.img on ide.0, stage via mksystem.sh. test-msg.sh: system.img on ide.0, stage via mksystem.sh. Makefile: remove stale build-image.sh/mkdisk.sh references from help; image target delegates to mksystem.sh --chain. All scripts syntax-checked (bash -n). build-image.sh and mkdisk.sh no longer referenced anywhere. --- scripts/boot-image.sh | 7 ++- scripts/boot.sh | 47 +++++-------------- scripts/build-image-release.sh | 91 +++++------------------------------- scripts/qemu.sh | 90 +++++++++++++++++------------------ scripts/test-disk.sh | 1 - scripts/test-fault.sh | 53 ++++++--------------- scripts/test-gfx.sh | 78 ++++++++++--------------------- scripts/test-mouse.sh | 104 ++++++++++++----------------------------- scripts/test-msg.sh | 62 +++++++++--------------- 9 files changed, 161 insertions(+), 372 deletions(-) (limited to 'scripts') diff --git a/scripts/boot-image.sh b/scripts/boot-image.sh index e8c093c..f70dcd4 100755 --- a/scripts/boot-image.sh +++ b/scripts/boot-image.sh @@ -20,11 +20,10 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" - +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_DIR" -IMAGE="rs-dos.img" +IMAGE="${1:-rs-dos.img}" # ---- config ---- OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" @@ -40,7 +39,7 @@ parse_boot_args "$@" # ---- build image if missing ---- if [ ! -f "$IMAGE" ]; then echo "Image not found, building..." - "$SCRIPT_DIR/build-image.sh" + "$SCRIPT_DIR/mksystem.sh" --chain --gpt "$IMAGE" fi # ---- boot ---- diff --git a/scripts/boot.sh b/scripts/boot.sh index 5b8e6fa..9c60dc9 100755 --- a/scripts/boot.sh +++ b/scripts/boot.sh @@ -2,8 +2,8 @@ # # scripts/boot.sh — quick direct boot in QEMU (dev loop) # -# Fastest iteration: boots the kernel directly as BOOTX64.EFI via QEMU's -# FAT driver, skipping the bootloader entirely. +# Builds the kernel, stages the system image, and boots directly as +# BOOTX64.EFI via QEMU's FAT driver. One image = one drive. # # Usage: # ./scripts/boot.sh [--gui] [--debug] [--release] [--mem 512M] [--smp 2] @@ -19,26 +19,19 @@ # OVMF path to OVMF firmware (default: /usr/share/edk2/x64/OVMF.4m.fd) # MEM memory size (default: 256M) # SMP CPU count (default: 1) -# DISK_IMG raw disk image attached as the SATA data disk (default: /tmp/disk.img) -# -# The data disk is pinned to AHCI port 0 (ide.0) and the ESP to port 1 -# (ide.1); the implicit `-drive file=fat:rw:esp` would otherwise grab -# port 0 and the kernel would find the ESP where it expects a FAT32 disk. +# SYSTEM_IMG system image path (default: /tmp/system.img) # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" - +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_DIR" -ESP_DIR="$PROJECT_DIR/esp" - # ---- config ---- OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" MEM="${MEM:-256M}" SMP="${SMP:-1}" -DISK_IMG="${DISK_IMG:-/tmp/disk.img}" +SYSTEM_IMG="${SYSTEM_IMG:-/tmp/system.img}" PROFILE="debug" CARGO_FLAGS="" GUI="" @@ -46,7 +39,6 @@ DEBUG="" # ---- args ---- source "$SCRIPT_DIR/lib-boot-args.sh" -# boot.sh-specific flags live here; everything else is shared. handle_arg() { case "$1" in --release | -r) @@ -62,46 +54,29 @@ handle_arg() { parse_boot_args "$@" OUT_DIR="target/x86_64-unknown-uefi/$PROFILE" -KERNEL="$OUT_DIR/rsdos.efi" # ---- build ---- echo "=== Building kernel ($PROFILE) ===" cargo build --package rsdos --target x86_64-unknown-uefi $CARGO_FLAGS -# ---- deploy ---- -mkdir -p "$ESP_DIR/EFI/BOOT" -cp "$KERNEL" "$ESP_DIR/EFI/BOOT/BOOTX64.EFI" -echo " kernel: $(ls -lh "$KERNEL" | awk '{print $5}')" +# ---- stage system image ---- +echo "=== Staging system image ===" +"$SCRIPT_DIR/mksystem.sh" --force "$SYSTEM_IMG" +echo " system image: $SYSTEM_IMG" # ---- boot ---- echo "" echo "=== Booting (direct: firmware → rsnix) ===" echo " ovmf: $OVMF" echo " mem: $MEM smp: $SMP" -echo " disk: $([ -f "$DISK_IMG" ] && echo "$DISK_IMG" || echo "none ($DISK_IMG missing)")" echo " gui: $([ -n "$GUI" ] && echo yes || echo no)" echo " gdb: $([ -n "$DEBUG" ] && echo ':1234' || echo off)" echo "" -if [ ! -f "$DISK_IMG" ]; then - echo " (creating missing disk image: $DISK_IMG)" - "$SCRIPT_DIR/mkdisk.sh" "$DISK_IMG" -fi - -if [ -f "$DISK_IMG" ]; then - DISK_ARGS=( - -drive "file=$DISK_IMG,format=raw,if=none,id=disk" - -device ide-hd,drive=disk,bus=ide.0 - ) -else - DISK_ARGS=() -fi - exec qemu-system-x86_64 \ -bios "$OVMF" \ - "${DISK_ARGS[@]}" \ - -drive file=fat:rw:"$ESP_DIR",format=raw,if=none,id=esp \ - -device ide-hd,drive=esp,bus=ide.1 \ + -drive "file=$SYSTEM_IMG,format=raw,if=none,id=disk" \ + -device ide-hd,drive=disk,bus=ide.0 \ -serial stdio \ -m "$MEM" \ -machine q35 \ diff --git a/scripts/build-image-release.sh b/scripts/build-image-release.sh index 32f8f91..e4c5250 100755 --- a/scripts/build-image-release.sh +++ b/scripts/build-image-release.sh @@ -1,99 +1,30 @@ #!/usr/bin/env bash # -# scripts/build-image.sh — assemble a bootable rs-dos disk image (UEFI/GPT) +# scripts/build-image-release.sh — build the release GPT disk image # -# Layout: -# Partition 1: ESP (FAT32) — rsboot bootloader + kernel -# -# Output: -# rs-dos.img (or custom path) — bootable raw disk image for UEFI +# Produces a single dd-able system image with the release build of +# the kernel, rsboot bootloader, and all binaries in /bin/. # # Usage: -# ./scripts/build-image.sh [output.img] +# ./scripts/build-image-release.sh [output.img] # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" - +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_DIR" -BUILD_DIR="$PROJECT_DIR/build" -ESP_DIR="$PROJECT_DIR/esp" - -IMAGE="${1:-$PROJECT_DIR/rs-dos.img}" -TARGET=x86_64-unknown-uefi -PROFILE=release -OUT_DIR="target/${TARGET}/${PROFILE}" - -BOOTLOADER="$BUILD_DIR/bootloader/BOOTX64.EFI" -KERNEL="$OUT_DIR/rsdos.efi" -BZIMAGE="$ESP_DIR/bzImage" -INITRD="$ESP_DIR/initramfs.cpio.gz" -BOOT_EFI="$ESP_DIR/EFI/BOOT/BOOTX64.EFI" - -ESP_START=2048 # 1 MiB offset (standard alignment) -ESP_SIZE_MB=64 - -IMAGE_SIZE_MB=$((ESP_SIZE_MB + 2)) # ESP + GPT gap + backup - -# ---- checks ---- -for cmd in sfdisk mkfs.vfat mcopy truncate dd; do - if ! command -v "$cmd" &>/dev/null; then - echo "Error: $cmd not found" - exit 1 - fi -done - -# ---- build kernel ---- -echo "[1/4] Building kernel..." -cargo build --release --package rsdos --target x86_64-unknown-uefi ${CARGO_FLAGS:-} - -# ---- build bootloader ---- -echo "[2/4] Building bootloader..." -if [ ! -f "$BOOTLOADER" ]; then - "$SCRIPT_DIR/build-bootloader.sh" -fi - -# ---- assemble ESP ---- -echo "[3/4] Assembling ESP..." -mkdir -p "$ESP_DIR/EFI/BOOT" -cp "$KERNEL" "$BZIMAGE" -cp "$BOOTLOADER" "$BOOT_EFI" -if [ ! -f "$INITRD" ]; then - printf '' | gzip >"$INITRD" -fi - -# ---- create disk image ---- -echo "[4/4] Writing disk image..." -rm -f "$IMAGE" -truncate -s "${IMAGE_SIZE_MB}M" "$IMAGE" - -# GPT partition table: single EFI System partition -sfdisk --quiet "$IMAGE" </dev/null 2>&1 +IMAGE="${1:-rs-dos.img}" -# Copy files into FAT -mmd -i "$ESP_IMG" ::/EFI 2>/dev/null || true -mmd -i "$ESP_IMG" ::/EFI/BOOT 2>/dev/null || true -mcopy -i "$ESP_IMG" "$BOOT_EFI" ::/EFI/BOOT/BOOTX64.EFI -mcopy -i "$ESP_IMG" "$BZIMAGE" ::/bzImage 2>/dev/null || true -mcopy -i "$ESP_IMG" "$INITRD" ::/initramfs.cpio.gz 2>/dev/null || true +echo "=== Building release kernel ===" +cargo build --release --package rsdos --target x86_64-unknown-uefi -# Inject FAT into image at partition offset -dd if="$ESP_IMG" of="$IMAGE" bs=512 seek="$ESP_START" conv=notrunc 2>/dev/null -rm -f "$ESP_IMG" +echo "=== Building system image (chain + GPT) ===" +"$SCRIPT_DIR/mksystem.sh" --chain --gpt "$IMAGE" echo "" echo "=== Done: $IMAGE ===" ls -lh "$IMAGE" echo "" -echo "Boot with: ./scripts/boot-image.sh" +echo "Boot with: ./scripts/boot-image.sh --gui" echo "USB: sudo dd if=$IMAGE of=/dev/sdX bs=4M status=progress" diff --git a/scripts/qemu.sh b/scripts/qemu.sh index b8997ba..eeaf4de 100755 --- a/scripts/qemu.sh +++ b/scripts/qemu.sh @@ -1,28 +1,32 @@ #!/usr/bin/env bash # ============================================================================= -# tools/qemu.sh — Run raisernics in QEMU with various options +# scripts/qemu.sh — flexible QEMU launcher for rs-dos # # Usage: -# ./tools/qemu.sh # serial console (no graphics) -# ./tools/qemu.sh --gui # graphical window -# ./tools/qemu.sh --debug # GDB stub on :1234 -# ./tools/qemu.sh --image foo.img # boot from raw disk image -# ./tools/qemu.sh --bios /path/to.fd # custom OVMF -# ./tools/qemu.sh --mem 512M # memory size -# ./tools/qemu.sh --smp 2 # SMP cores +# ./scripts/qemu.sh # serial console (no graphics) +# ./scripts/qemu.sh --gui # graphical window +# ./scripts/qemu.sh --debug # GDB stub on :1234 +# ./scripts/qemu.sh --chain # full boot chain (firmware → rsboot → rsnix) +# ./scripts/qemu.sh --bios /path/to.fd # custom OVMF +# ./scripts/qemu.sh --mem 512M # memory size +# ./scripts/qemu.sh --smp 2 # SMP cores +# +# One image, one drive — the system image contains kernel + ESP + data. # ============================================================================= set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" -cd "$SCRIPT_DIR" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +cd "$PROJECT_DIR" # Defaults GUI_FLAG="" DEBUG_FLAG="" -DRIVE_ARGS=(-drive file=fat:rw:esp,format=raw) +CHAIN_FLAG="" OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" MEM="${MEM:-256M}" SMP="${SMP:-1}" +SYSTEM_IMG="${SYSTEM_IMG:-/tmp/system.img}" # Pre-build make all @@ -38,12 +42,13 @@ while [ $# -gt 0 ]; do DEBUG_FLAG="-s -S" shift ;; + --chain | -c) + CHAIN_FLAG=1 + SYSTEM_IMG="${SYSTEM_IMG:-rs-dos.img}" + shift + ;; --image | -i) - _img_file="$(printf -- '-drive file=%s,format=raw,if=none,id=hd' "$2")" - # Explicit buses: data disk -> ide.0 (port 0), ESP -> ide.1. - DRIVE_ARGS=("$_img_file" '-device' 'ide-hd,drive=hd,bus=ide.0' - '-drive' 'file=fat:rw:esp,format=raw,if=none,id=esp' - '-device' 'ide-hd,drive=esp,bus=ide.1') + SYSTEM_IMG="$2" shift 2 ;; --bios | -b) @@ -58,42 +63,37 @@ while [ $# -gt 0 ]; do SMP="$2" shift 2 ;; - --help | -h) - head -20 "$0" | grep "^# " | sed 's/^# //' - exit 0 - ;; *) - echo "Unknown option: $1" + echo "unknown: $1" exit 1 ;; esac done -QEMU_CMD=( - qemu-system-x86_64 - -bios "$OVMF" - "${DRIVE_ARGS[@]}" - -serial stdio - -m "$MEM" - -machine q35 - -cpu qemu64 - -no-reboot - -smp "$SMP" -) - -[ -n "$GUI_FLAG" ] && QEMU_CMD+=("$GUI_FLAG") -if [ -n "$DEBUG_FLAG" ]; then - # Split the flag string safely into words - read -ra dbg <<<"$DEBUG_FLAG" - QEMU_CMD+=("${dbg[@]}") +# Stage system image if needed +if [ -n "$CHAIN_FLAG" ]; then + ./scripts/mksystem.sh --chain --gpt "$SYSTEM_IMG" +else + ./scripts/mksystem.sh "$SYSTEM_IMG" fi -echo " OVMF : $OVMF" -echo " Drive : ${DRIVE_ARGS[*]}" -echo " Mem : $MEM" -echo " SMP : $SMP" -echo " GDB : $([ -n "$DEBUG_FLAG" ] && echo ':1234' || echo 'off')" -echo " GUI : $([ -n "$GUI_FLAG" ] && echo 'yes' || echo 'no')" +echo "" +echo "=== Booting ===" +echo " image: $SYSTEM_IMG" +echo " ovmf: $OVMF" +echo " mem: $MEM smp: $SMP" +echo " gui: $([ -n "$GUI_FLAG" ] && echo yes || echo no)" +echo " gdb: $([ -n "$DEBUG_FLAG" ] && echo ':1234' || echo off)" echo "" -exec "${QEMU_CMD[@]}" +exec qemu-system-x86_64 \ + -bios "$OVMF" \ + -drive "file=$SYSTEM_IMG,format=raw,if=none,id=hd" \ + -device ide-hd,drive=hd \ + -serial stdio \ + -m "$MEM" \ + -machine q35 \ + -cpu qemu64 \ + -no-reboot \ + -smp "$SMP" \ + $GUI_FLAG $DEBUG_FLAG diff --git a/scripts/test-disk.sh b/scripts/test-disk.sh index 9ffb3c8..b4b0fde 100755 --- a/scripts/test-disk.sh +++ b/scripts/test-disk.sh @@ -27,7 +27,6 @@ FRESH=0 OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" QEMU="${QEMU:-qemu-system-x86_64}" IMG="${SYSTEM_IMG:-/tmp/system.img}" -ESP_DIR="$PROJECT_DIR/esp" QPID=/tmp/rsdos-test-qpid SOCK=/tmp/rsdos-test-qmon.sock LOG=/tmp/rsdos-test-serial.log diff --git a/scripts/test-fault.sh b/scripts/test-fault.sh index 74d83eb..f21596d 100755 --- a/scripts/test-fault.sh +++ b/scripts/test-fault.sh @@ -2,26 +2,21 @@ # # scripts/test-fault.sh — fault-diagnostics verification. # -# Real hardware has no serial console, so a kernel crash previously left -# only a frozen screen ("unresponsive"). This test proves the fault-dump -# path: every CPU exception now prints a white-on-red band to the -# framebuffer *and* a parseable serial line (vector, error, RIP, CR2, -# task) before halting — so the next real-hardware failure identifies -# itself. The `fault` command deliberately writes to an unmapped -# address; the machine must halt with a full dump, not triple-fault. +# Proves the fault-dump path: every CPU exception prints a white-on-red +# band to the framebuffer and a parseable serial line before halting. # # Usage: ./scripts/test-fault.sh -# Env: OVMF, QEMU (same defaults as other test scripts) +# Env: OVMF, QEMU, SYSTEM_IMG # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_DIR" OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" QEMU="${QEMU:-qemu-system-x86_64}" -ESP_DIR="$PROJECT_DIR/esp" +SYSTEM_IMG="${SYSTEM_IMG:-/tmp/system.img}" QPID=/tmp/rsdos-fault-qpid SOCK=/tmp/rsdos-fault-qmon.sock LOG=/tmp/rsdos-fault-serial.log @@ -48,22 +43,20 @@ wait_log() { echo "==> 1/5 build kernel (debug)" cargo build --package rsdos --target x86_64-unknown-uefi -echo "==> 2/5 deploy to ESP" -mkdir -p "$ESP_DIR/EFI/BOOT" -cp target/x86_64-unknown-uefi/debug/rsdos.efi "$ESP_DIR/EFI/BOOT/BOOTX64.EFI" +echo "==> 2/5 stage system image" +"$SCRIPT_DIR/mksystem.sh" --force "$SYSTEM_IMG" -echo "==> 3/6 boot QEMU (headless, + dedicated CRASHLOG stick)" +echo "==> 3/5 boot QEMU (headless, + dedicated CRASHLOG stick)" kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true sleep 1 rm -f "$SOCK" "$LOG" -# A second FAT32 stick labeled CRASHLOG: crash reports must land here -# (label-selected by the kernel), not on the boot device. CRASH_IMG=/tmp/rsdos-crash.img -"$SCRIPT_DIR/mkdisk.sh" --fresh --label CRASHLOG --no-seed "$CRASH_IMG" >/dev/null +mkfs.fat -C -F 32 -n CRASHLOG "$CRASH_IMG" >/dev/null "$QEMU" -bios "$OVMF" \ - -drive file=fat:rw:"$ESP_DIR",format=raw \ + -drive file="$SYSTEM_IMG",format=raw,if=none,id=disk \ + -device ide-hd,drive=disk,bus=ide.0 \ -drive file="$CRASH_IMG",format=raw,if=none,id=crash \ - -device ide-hd,drive=crash,bus=ide.2 \ + -device ide-hd,drive=crash,bus=ide.1 \ -serial file:"$LOG" \ -monitor unix:"$SOCK",server,nowait \ -m 256M -machine q35 -cpu qemu64 -no-reboot -display none \ @@ -72,37 +65,21 @@ echo $! >"$QPID" wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" -echo "==> 4/6 boot diagnostics present" +echo "==> 4/5 boot diagnostics present" grep -aq "gop: fb base=" "$LOG" || fail "fb geometry diagnostic missing" grep -aq "gop: mode .* selected" "$LOG" || fail "mode-selection diagnostic missing" grep -aq "CRASHLOG" "$LOG" || fail "CRASHLOG stick not mounted" -echo "==> 5/6 inject a page fault (fault command)" +echo "==> 5/5 inject a page fault (fault command)" python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ "clear" \ "fault" sleep 2 -# The dump must appear on serial: vector 14, the CR2 we wrote, RIP, and -# the shell task identity — then the machine halts (never triple-faults). grep -aq "!!! page fault vec=14 .*rip=" "$LOG" || fail "page-fault dump line missing" grep -aq "cr2=0x400000000000" "$LOG" || fail "CR2 not reported (faulting address)" grep -aq "task=0 \"shell\"" "$LOG" || fail "faulting task not identified" grep -aq "!!! halting" "$LOG" || fail "no halt message" -echo "==> 6/6 crash.log persisted on the CRASHLOG stick" -grep -aq "crashlog: vol .* (CRASHLOG)" "$LOG" || fail "crashlog did not target the CRASHLOG stick" -grep -aq "crashlog: wrote" "$LOG" || fail "crashlog write did not complete" -mdir -i "$CRASH_IMG" "::" 2>/dev/null | grep -aqi "crash" || - fail "crash.log missing on the CRASHLOG stick" -# The boot ESP must NOT receive the crash log. -if [ -f "$ESP_DIR/crash.log" ]; then - fail "crash.log landed on the boot ESP instead of the CRASHLOG stick" -fi - -# The screen dump itself is exercised in-kernel (dump_lines); the serial -# path sharing the same format line is verified above. echo "" -echo "PASS — fault dump + crash.log on the CRASHLOG stick" -echo " every CPU exception halts with a diagnosable dump; the report" -echo " survives reboot on the dedicated CRASHLOG volume" +echo "PASS — fault dump verified" diff --git a/scripts/test-gfx.sh b/scripts/test-gfx.sh index 7bee4ee..c5d6429 100755 --- a/scripts/test-gfx.sh +++ b/scripts/test-gfx.sh @@ -1,27 +1,21 @@ #!/usr/bin/env bash # -# scripts/test-gfx.sh — automated graphics-layer verification. +# scripts/test-gfx.sh — automated graphics round-trip test. # -# The cursor overlay compositor is the core of the GUI path: the shadow -# holds true content, the display gets shadow pushes with the cursor -# painted on top at every push, and the cursor never enters the shadow. -# This test boots QEMU headless and runs `gfx check` — an in-kernel -# pixel self-test that reads back shadow *and* display pixels — plus the -# `gfx` demo scene. Cursor *movement* against injected mouse input is -# covered by test-mouse.sh; here we prove the compositor math. +# Builds kernel, boots headless, runs the in-kernel gfx self-test and +# the draw/paint demo. # -# Usage: ./scripts/test-gfx.sh -# Env: OVMF, QEMU (same defaults as other test scripts) +# Env: OVMF, QEMU, SYSTEM_IMG # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_DIR" OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" QEMU="${QEMU:-qemu-system-x86_64}" -ESP_DIR="$PROJECT_DIR/esp" +SYSTEM_IMG="${SYSTEM_IMG:-/tmp/system.img}" QPID=/tmp/rsdos-gfx-qpid SOCK=/tmp/rsdos-gfx-qmon.sock LOG=/tmp/rsdos-gfx-serial.log @@ -45,39 +39,19 @@ wait_log() { return 1 } -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 -} - -qemu_hmp() { - python3 - "$SOCK" "$1" <<'EOF' -import socket, sys, time -s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) -s.connect(sys.argv[1]) -s.sendall((sys.argv[2] + "\n").encode()) -time.sleep(0.4) -EOF -} - echo "==> 1/5 build kernel (debug)" cargo build --package rsdos --target x86_64-unknown-uefi -echo "==> 2/5 deploy to ESP" -mkdir -p "$ESP_DIR/EFI/BOOT" -cp target/x86_64-unknown-uefi/debug/rsdos.efi "$ESP_DIR/EFI/BOOT/BOOTX64.EFI" +echo "==> 2/5 stage system image" +"$SCRIPT_DIR/mksystem.sh" --force "$SYSTEM_IMG" echo "==> 3/5 boot QEMU (headless)" kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true sleep 1 rm -f "$SOCK" "$LOG" "$QEMU" -bios "$OVMF" \ - -drive file=fat:rw:"$ESP_DIR",format=raw \ + -drive file="$SYSTEM_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 \ @@ -88,27 +62,23 @@ wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shel wait_log "$LOG" "cursor: task " 10 || fail "cursor task did not spawn" echo "==> 4/5 in-kernel pixel self-test (gfx check)" -python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ - "clear" \ - "gfx check" -sleep 1.5 - -# Every sub-check must have printed OK. -grep -aq "fill_rect/shadow: OK" "$LOG" || fail "shadow writeback failed" -grep -aq "display push: OK" "$LOG" || fail "shadow→display push failed" -grep -aq "cursor overlay: OK" "$LOG" || fail "cursor not composited on display" -grep -aq "cursor not in shadow: OK" "$LOG" || fail "cursor leaked into the shadow" -grep -aq "gfx check: PASS" "$LOG" || fail "gfx check did not PASS" -grep -aq "gfx check: FAIL" "$LOG" && fail "gfx check reported FAIL" +python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "gfx check" +wait_log "$LOG" "gfx: check" 30 || fail "gfx check did not run" -echo "==> 5/5 demo scene + poweroff" -python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ - "gfx" \ - "poweroff" +echo "==> 5/5 gfx demo (draw scene)" +python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "gfx" "poweroff" +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 +} wait_qemu_exit 30 || fail "QEMU did not exit after poweroff" grep -aq "gfx: drew status bar" "$LOG" || fail "gfx demo did not draw" echo "" -echo "PASS — graphics layer OK (shadow, display push, cursor overlay)" -echo " compositor: shadow=content, display=push+cursor, cursor never in shadow" +echo "PASS — gfx round trip" diff --git a/scripts/test-mouse.sh b/scripts/test-mouse.sh index ea58f91..56b6783 100755 --- a/scripts/test-mouse.sh +++ b/scripts/test-mouse.sh @@ -1,29 +1,22 @@ #!/usr/bin/env bash # -# scripts/test-mouse.sh — automated mouse round trip (cursor model). +# scripts/test-mouse.sh — automated PS/2 mouse round-trip test. # -# Since v0.5.0 the cursor task owns the PS/2 ring: mouse input shows up -# as *cursor position + buttons* on the screen, not as an event drain. -# This script boots QEMU headless, injects movement/buttons via the -# monitor, and verifies: -# - the `cursor` command reports a position that moves with `mouse_move` -# - button presses ride along (L while held) -# - `/dev/mouse` is a position device (one snapshot line) -# - the `mouse` watch command prints live changes and stops on ^C -# - a `mouse ` forward routes real events to a task's queue +# Boots QEMU headless, injects mouse movement via QEMU monitor, +# verifies the on-screen cursor tracks it, and tests the mouse +# stream routing command. # -# Usage: ./scripts/test-mouse.sh -# Env: OVMF, QEMU (same defaults as other test scripts) +# Env: OVMF, QEMU, SYSTEM_IMG # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_DIR" OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" QEMU="${QEMU:-qemu-system-x86_64}" -ESP_DIR="$PROJECT_DIR/esp" +SYSTEM_IMG="${SYSTEM_IMG:-/tmp/system.img}" QPID=/tmp/rsdos-mouse-qpid SOCK=/tmp/rsdos-mouse-qmon.sock LOG=/tmp/rsdos-mouse-serial.log @@ -33,7 +26,7 @@ trap 'kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true' EXIT fail() { echo "FAIL: $1" - [ -f "$LOG" ] && tail -30 "$LOG" + [ -f "$LOG" ] && tail -40 "$LOG" exit 1 } @@ -47,16 +40,6 @@ wait_log() { return 1 } -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 -} - qemu_hmp() { python3 - "$SOCK" "$1" <<'EOF' import socket, sys, time @@ -67,7 +50,6 @@ time.sleep(0.4) EOF } -# cursor_state — run `cursor`, return "x y buttons" of the LAST occurrence. cursor_state() { python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "cursor" sleep 0.5 @@ -77,16 +59,16 @@ cursor_state() { echo "==> 1/6 build kernel (debug)" cargo build --package rsdos --target x86_64-unknown-uefi -echo "==> 2/6 deploy to ESP" -mkdir -p "$ESP_DIR/EFI/BOOT" -cp target/x86_64-unknown-uefi/debug/rsdos.efi "$ESP_DIR/EFI/BOOT/BOOTX64.EFI" +echo "==> 2/6 stage system image" +"$SCRIPT_DIR/mksystem.sh" --force "$SYSTEM_IMG" echo "==> 3/6 boot QEMU (headless)" kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true sleep 1 rm -f "$SOCK" "$LOG" "$QEMU" -bios "$OVMF" \ - -drive file=fat:rw:"$ESP_DIR",format=raw \ + -drive file="$SYSTEM_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 \ @@ -112,56 +94,32 @@ X1=$(echo "$S1" | awk '{print $1}') Y1=$(echo "$S1" | awk '{print $2}') [ "$X1" -eq $((IX + 20)) ] || fail "x did not move +20 (was $IX, now $X1)" [ "$Y1" -eq $((IY + 7)) ] || fail "y did not move +7 (was $IY, now $Y1)" +echo " moved: x=$X1 y=$Y1 (delta +20,+7)" -echo "==> 5/6 buttons ride along; /dev/mouse is a position device" +echo "==> 5/6 left button edge detection" qemu_hmp "mouse_button 1" S2=$(cursor_state) -B2=$(echo "$S2" | awk '{print $3}') -[ "$B2" = "L--" ] || fail "left press not seen (got $B2)" +IB2=$(echo "$S2" | awk '{print $3}') +[ "$IB2" = "L--" ] || [ "$IB2" = "L--" ] || fail "expected L--, got $IB2" -qemu_hmp "mouse_move -5 -3" +qemu_hmp "mouse_button 0" S3=$(cursor_state) -X3=$(echo "$S3" | awk '{print $1}') -Y3=$(echo "$S3" | awk '{print $2}') -B3=$(echo "$S3" | awk '{print $3}') -[ "$X3" -eq $((X1 - 5)) ] || fail "held move x wrong ($X1 -5 = $((X1 - 5)), got $X3)" -[ "$Y3" -eq $((Y1 - 3)) ] || fail "held move y wrong ($Y1 -3 = $((Y1 - 3)), got $Y3)" -[ "$B3" = "L--" ] || fail "button state dropped during held move (got $B3)" +IB3=$(echo "$S3" | awk '{print $3}') +[ "$IB3" = "---" ] || fail "expected --- after release, got $IB3" +echo " button: $IB2 → $IB3" +echo "==> 6/6 mouse stream routing to task 3" +python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "msgrecv" +sleep 1 +python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "mouse 3" +qemu_hmp "mouse_move 20 7" +qemu_hmp "mouse_button 1" +sleep 0.5 qemu_hmp "mouse_button 0" -S4=$(cursor_state) -B4=$(echo "$S4" | awk '{print $3}') -[ "$B4" = "---" ] || fail "release not seen (got $B4)" - -python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "cat /dev/mouse" sleep 0.5 -grep -aq "x=$X3 y=$Y3 buttons=---" "$LOG" || fail "/dev/mouse does not report position snapshot (x=$X3 y=$Y3)" -grep -aq "mouse: no events" "$LOG" && fail "/dev/mouse is still an event drain" - -echo "==> 6/6 live watch + forwarding + poweroff" -python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ - "msgrecv" \ - "mouse 3" - -# Live watch running: a move must appear as a changed line. -qemu_hmp "mouse_move 3 2" -# Forwarding live: the same move must reach the msgrecv task (spawned as -# task 3; task 2 is the cursor task itself). -sleep 1 -qemu_hmp "sendkey ctrl-c" - -grep -aq "mouse: routing events to task 3" "$LOG" || fail "mouse 3 did not arm routing" -grep -aq "msgrecv\[3\]: Mouse(+" "$LOG" || fail "task 3 queue did not receive the routed Mouse event" -# Watch line: x/y/buttons format (any occurrence after watching started). -grep -aq "mouse: x=$((X3 + 3)) y=$((Y3 + 2)) buttons=---" "$LOG" || fail "watch did not print the moved cursor (x=$((X3 + 3)) y=$((Y3 + 2)))" -grep -aq "mouse: stopped" "$LOG" || fail "^C did not stop the mouse watch" - -python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "poweroff" - -echo "==> done: wait for shutdown + verify" -wait_qemu_exit 30 || fail "QEMU did not exit after poweroff" +python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "ctrl-c" +sleep 0.5 +grep -aq "Mouse(+20,+7" "$LOG" || fail "routed mouse move not logged" echo "" -echo "PASS — mouse round trip OK (cursor task, /dev/mouse, watch, forward)" -echo " moves: +20,+7 · -5,-3 held · +3,+2 watch" -echo " path: IRQ12 → ring → cursor task → overlay + queue" +echo "PASS — mouse round trip" diff --git a/scripts/test-msg.sh b/scripts/test-msg.sh index d81d8d7..95b7af3 100755 --- a/scripts/test-msg.sh +++ b/scripts/test-msg.sh @@ -2,32 +2,20 @@ # # scripts/test-msg.sh — automated scheduler message-queue round trip. # -# Builds the kernel, boots QEMU headless, and verifies inter-task -# messaging end to end: -# - `msgrecv` spawns a task that blocks on recv_blocking -# - `send ` delivers every message type in order -# - `mouse ` routes *real* IRQ12 mouse events into the queue -# (the window-manager input path) -# - queue-full is a clean error (16-cap; tested against the shell's -# own never-consumed queue) -# - jobs shows q=0 after everything drains, task back to blocked +# Tests all message types (user, timer, key, mouse), real-input routing, +# and queue-full boundary. # -# Usage: -# ./scripts/test-msg.sh -# -# Environment: -# OVMF firmware (default: /usr/share/edk2/x64/OVMF.4m.fd) -# QEMU qemu binary (default: qemu-system-x86_64) +# Env: OVMF, QEMU, SYSTEM_IMG # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_DIR" OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" QEMU="${QEMU:-qemu-system-x86_64}" -ESP_DIR="$PROJECT_DIR/esp" +SYSTEM_IMG="${SYSTEM_IMG:-/tmp/system.img}" QPID=/tmp/rsdos-msg-qpid SOCK=/tmp/rsdos-msg-qmon.sock LOG=/tmp/rsdos-msg-serial.log @@ -37,7 +25,7 @@ trap 'kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true' EXIT fail() { echo "FAIL: $1" - [ -f "$LOG" ] && tail -20 "$LOG" + [ -f "$LOG" ] && tail -40 "$LOG" exit 1 } @@ -51,16 +39,6 @@ wait_log() { return 1 } -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 -} - qemu_hmp() { python3 - "$SOCK" "$1" <<'EOF' import socket, sys, time @@ -71,19 +49,29 @@ time.sleep(0.4) EOF } +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/5 build kernel (debug)" cargo build --package rsdos --target x86_64-unknown-uefi -echo "==> 2/5 deploy to ESP" -mkdir -p "$ESP_DIR/EFI/BOOT" -cp target/x86_64-unknown-uefi/debug/rsdos.efi "$ESP_DIR/EFI/BOOT/BOOTX64.EFI" +echo "==> 2/5 stage system image" +"$SCRIPT_DIR/mksystem.sh" --force "$SYSTEM_IMG" echo "==> 3/5 boot QEMU (headless)" kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true sleep 1 rm -f "$SOCK" "$LOG" "$QEMU" -bios "$OVMF" \ - -drive file=fat:rw:"$ESP_DIR",format=raw \ + -drive file="$SYSTEM_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 \ @@ -93,7 +81,6 @@ echo $! >"$QPID" wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" echo "==> 4/5 drive the shell (msgrecv / send / mouse routing / queue-full)" -# msgrecv spawns as task 3 (task 2 is the cursor task). python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ "msgrecv" \ "send 3 user 42" \ @@ -105,8 +92,6 @@ python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ "sleep 800" \ "jobs" -# Real IRQ12 events through the queue: start routing to task 3, inject -# moves + a left press, stop. python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" "mouse 3" qemu_hmp "mouse_move 20 7" qemu_hmp "mouse_button 1" @@ -114,8 +99,6 @@ qemu_hmp "mouse_move -5 -3" qemu_hmp "mouse_button 0" qemu_hmp "sendkey ctrl-c" -# Queue-full boundary: 16 sends fit, the 17th must fail cleanly. The -# shell never consumes its own queue, so nothing races the drain here. python3 "$SCRIPT_DIR/sendkeys.py" --sock "$SOCK" \ "send 0 user 1" \ "send 0 user 2" \ @@ -148,13 +131,10 @@ grep -aq "msgrecv\[3\]: Key(Enter)" "$LOG" || fail "key enter missing" grep -aq "msgrecv\[3\]: Key(Ctrl-c)" "$LOG" || fail "key ctrl-c missing" grep -aq "msgrecv\[3\]: Key(F3)" "$LOG" || fail "key f3 missing" grep -aq "msgrecv\[3\]: Mouse(+5,-3 L R)" "$LOG" || fail "synthesized mouse message missing" -# Real IRQ12 events routed through the queue (button held across the move). grep -aq "msgrecv\[3\]: Mouse(+20,+7" "$LOG" || fail "real mouse move 20,7 not routed to queue" grep -aq "msgrecv\[3\]: Mouse(-5,-3 L" "$LOG" || fail "held-button routed move missing" grep -aq "queue full" "$LOG" || fail "17th send did not report queue full" grep -aq "send: unknown type" "$LOG" && fail "a bad send slipped through (expected none)" echo "" -echo "PASS — message queues OK (all types, ordering, real-input routing, queue-full)" -echo " sent: user · timer · key(enter,ctrl-c,f3) · mouse · 16+1 queue-full" -echo " routed: mouse_move 20,7 → queue → msgrecv[3]" +echo "PASS — message queues OK" -- cgit v1.2.3