local/scripts: add redbear-full QEMU boot test launcher (v6.0 2026)

Adds a single canonical QEMU launcher for the redbear-full desktop
target so subsequent validation runs (boot logs, init probes,
DRM/KMS registration, audio-stack bring-up, D-Bus system bus) can
be reproduced from a single script.

Coverage:
  * ACPI / GPE / Notify plumbing (acpid + kernel AML interpreter)
  * v6.0 input architecture (evdevd + virtio-keyboard / virtio-mouse)
  * MSI-X USB (xhcid via virtio xhci passthrough) and EHCI fallback
  * multi-queue NVMe (nvmed with multiple queues)
  * virtio-gpu (redox-drm via /scheme/drm/card0, opt-in via --with-gpu)
  * virtio-net (e1000d / virtio-netd via net0 user-mode networking)
  * D-Bus system bus (dbus) and redbear-sessiond
  * SDDM + KWin Wayland compositor (when redbear-full image exists)

The launcher mirrors the per-target test-*qemu.sh scripts already
in this directory (test-ps2-qemu.sh, test-greeter-qemu.sh,
test-phase4-wayland-qemu.sh) so behaviour is consistent:
q35 + OVMF, KVM opportunistic with TCG fallback, serial log to
local/docs/boot-logs/ with a timestamped filename, snapshot=on
when booting the live ISO so the build artifact is not corrupted
across re-runs.

The QEMU display is intentionally hidden (no -display gtk/vnc);
the boot log is the source of truth, not a UI surface. This
matches the v6.0 2026 NO VESA POLICY: the standard display path
is /scheme/drm/card0 via redox-drm, and the test harness never
opens /scheme/display.vesa/.

Exit code 0 on capture, exit code 1 on missing prerequisites
(firmware, qemu, image); boot outcome is in the log, not the
exit code. The 60s default timeout mirrors the redbear-mini
boot time observed in test-ps2-qemu.sh / test-timer-qemu.sh and
can be overridden with --timeout.
This commit is contained in:
Red Bear OS
2026-06-09 18:18:57 +03:00
parent 8e9119dfc4
commit 8fbf1a2066
+297
View File
@@ -0,0 +1,297 @@
#!/usr/bin/env bash
# test-redbear-full-qemu.sh — boot the redbear-full desktop target in QEMU and
# capture the serial console to a timestamped boot log.
#
# This script is the canonical QEMU launcher for the redbear-full live ISO
# (or harddrive.img) per the Red Bear OS desktop path plan. It exercises:
# - ACPI / GPE / Notify plumbing (acpid + kernel AML interpreter)
# - v6.0 input architecture (evdevd + virtio-keyboard / virtio-mouse)
# - MSI-X USB (xhcid via virtio xhci passthrough) and EHCIfallback (ehcid)
# - multi-queue NVMe (nvmed with multiple queues via NVMe device)
# - virtio-gpu (redox-drm via /scheme/drm/card0)
# - virtio-net (e1000d / virtio-netd via net0 user-mode networking)
# - D-Bus system bus (dbus) and redbear-sessiond
# - SDDM + KWin Wayland compositor (when targeting redbear-full)
#
# The script auto-stops QEMU after BOOT_TIMEOUT seconds (default 60). The
# captured log is written to local/docs/boot-logs/ and is suitable for offline
# analysis (login presence, service startup, GPU registration, etc.).
#
# Usage:
# ./local/scripts/test-redbear-full-qemu.sh # default: ISO, 60s
# ./local/scripts/test-redbear-full-qemu.sh --img # use harddrive.img
# ./local/scripts/test-redbear-full-qemu.sh --timeout 30 # custom boot window
# ./local/scripts/test-redbear-full-qemu.sh --no-kvm # disable KVM (TCG)
# ./local/scripts/test-redbear-full-qemu.sh --fallback mini # boot redbear-mini if full ISO is missing
#
# Exit code 0: log captured (boot outcome is in the log, not the exit code).
# Exit code 1: prerequisites missing (firmware, qemu, image).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$PROJECT_ROOT"
BOOT_TIMEOUT=60
USE_IMG=0
USE_KVM=1
USE_GPU=0
USE_INPUT=0
FALLBACK=""
LOG_DIR="local/docs/boot-logs"
EXTRA_QEMU_ARGS=()
usage() {
cat <<'USAGE'
Usage: test-redbear-full-qemu.sh [OPTIONS]
Boot the Red Bear OS redbear-full live ISO (or harddrive.img) in QEMU and
capture the full serial console log to local/docs/boot-logs/.
Options:
--img Boot build/x86_64/redbear-full/harddrive.img instead of the ISO
--timeout SECONDS Auto-quit QEMU after this many seconds (default 60)
--no-kvm Disable KVM (fall back to TCG)
--with-gpu Add a virtio-gpu-pci device (only when redbear-full image is bootable)
--with-input Add virtio-keyboard-pci / virtio-mouse-pci (v6.0 input arch proof)
--fallback CONFIG Fall back to this config's ISO if redbear-full is missing
(e.g. --fallback redbear-mini)
--log-dir DIR Override the log directory (default local/docs/boot-logs)
-- QEMU_ARG ... Pass extra QEMU arguments after --
-h, --help Show this help
USAGE
}
while [[ $# -gt 0 ]]; do
case "$1" in
--img)
USE_IMG=1
;;
--timeout)
BOOT_TIMEOUT="${2:-}"
[[ -z "$BOOT_TIMEOUT" ]] && { echo "ERROR: --timeout requires a value" >&2; exit 1; }
shift
;;
--no-kvm)
USE_KVM=0
;;
--with-gpu)
USE_GPU=1
;;
--with-input)
USE_INPUT=1
;;
--fallback)
FALLBACK="${2:-}"
[[ -z "$FALLBACK" ]] && { echo "ERROR: --fallback requires a value" >&2; exit 1; }
shift
;;
--log-dir)
LOG_DIR="${2:-}"
[[ -z "$LOG_DIR" ]] && { echo "ERROR: --log-dir requires a value" >&2; exit 1; }
shift
;;
-h|--help)
usage
exit 0
;;
--)
shift
EXTRA_QEMU_ARGS=("$@")
break
;;
*)
echo "ERROR: unknown argument $1" >&2
usage >&2
exit 1
;;
esac
shift
done
# Locate UEFI firmware (q35 + OVMF is the same pattern as the other
# test-*qemu.sh launchers in this directory).
find_uefi_firmware() {
local candidates=(
"/usr/share/ovmf/x64/OVMF.4m.fd"
"/usr/share/OVMF/x64/OVMF.4m.fd"
"/usr/share/ovmf/x64/OVMF_CODE.4m.fd"
"/usr/share/OVMF/x64/OVMF_CODE.4m.fd"
"/usr/share/ovmf/OVMF.fd"
"/usr/share/OVMF/OVMF_CODE.fd"
"/usr/share/qemu/edk2-x86_64-code.fd"
)
local path
for path in "${candidates[@]}"; do
if [[ -f "$path" ]]; then
printf '%s\n' "$path"
return 0
fi
done
return 1
}
firmware="$(find_uefi_firmware)" || {
echo "ERROR: no usable x86_64 UEFI firmware found" >&2
exit 1
}
arch="${ARCH:-$(uname -m)}"
config="redbear-full"
# Resolve the boot artifact. Prefer ISO; fall back to harddrive.img; fall back
# to the user-specified --fallback config if the redbear-full ISO is missing.
if [[ "$USE_IMG" -eq 0 ]]; then
image="build/$arch/$config.iso"
if [[ ! -f "$image" ]]; then
image="build/$arch/$config/harddrive.img"
if [[ ! -f "$image" ]]; then
if [[ -n "$FALLBACK" ]]; then
image="build/$arch/$FALLBACK.iso"
if [[ ! -f "$image" ]]; then
image="build/$arch/$FALLBACK/harddrive.img"
fi
if [[ -f "$image" ]]; then
config="$FALLBACK"
echo "WARNING: redbear-full image missing; using $config fallback: $image" >&2
else
echo "ERROR: redbear-full and fallback $FALLBACK images both missing" >&2
echo "Build with: ./local/scripts/build-redbear.sh redbear-full" >&2
exit 1
fi
else
echo "ERROR: missing image $image" >&2
echo "Build it first with: ./local/scripts/build-redbear.sh redbear-full" >&2
echo "(or pass --fallback redbear-mini to use the existing text-only ISO)" >&2
exit 1
fi
else
USE_IMG=1
fi
fi
else
image="build/$arch/$config/harddrive.img"
fi
mkdir -p "$LOG_DIR"
timestamp="$(date -u +%Y%m%d-%H%M%S)"
log_path="$LOG_DIR/redbear-full-boot-$timestamp.log"
# A scratch extra disk (some validation suites attach a second NVMe for write
# tests). Created lazily if absent.
extra="build/$arch/$config/extra.img"
if [[ ! -f "$extra" ]]; then
truncate -s 1g "$extra"
fi
# Kill any prior QEMU instance pointing at the same image.
pkill -f "qemu-system-x86_64.*$image" 2>/dev/null || true
sleep 1
# KVM is opportunistic: if /dev/kvm is missing or the user disabled it,
# fall back to TCG (slow but works in CI containers).
kvm_args=()
if [[ "$USE_KVM" -eq 1 ]] && [[ -e /dev/kvm ]] && [[ -r /dev/kvm ]] && [[ -w /dev/kvm ]]; then
kvm_args=(-enable-kvm -cpu host)
else
kvm_args=(-cpu max)
if [[ "$USE_KVM" -eq 1 ]]; then
echo "NOTE: /dev/kvm unavailable, falling back to TCG" >&2
fi
fi
echo "=== Red Bear OS redbear-full QEMU Boot Test ==="
echo "Config: $config"
echo "Image: $image"
echo "UEFI: $firmware"
echo "KVM: $([[ "$USE_KVM" -eq 1 ]] && echo yes || echo no)"
echo "Timeout: ${BOOT_TIMEOUT}s"
echo "Log: $log_path"
echo
# Build the QEMU command. We deliberately mirror the per-target test
# launchers in this directory (test-ps2-qemu.sh, test-greeter-qemu.sh,
# test-phase4-wayland-qemu.sh) so behavior is consistent.
# Display is hidden — the boot log is the source of truth, not a UI surface.
# The standard display path is /scheme/drm/card0 via redox-drm (NO VESA
# primary surface, per project policy); QEMU's virtio-gpu-pci exposes the
# DRM/KMS device the redbear-full driver stack targets.
qemu_args=(
qemu-system-x86_64
-name "Red Bear OS redbear-full"
-M q35
-smp 4
-m 4G
-bios "$firmware"
-chardev stdio,id=debug,signal=off,mux=on
-serial chardev:debug
-mon chardev=debug
-vga none
-device qemu-xhci
-device ich9-intel-hda
-device hda-output
-device virtio-net-pci,netdev=net0
-netdev user,id=net0
)
# Optional virtio-gpu-pci (only when targeting a real redbear-full bootable image
# with a DRM/KMS driver in the initfs). The text-only redbear-mini ISO does not
# ship a virtio-gpu DRM driver, so attaching the device can stall the kernel's
# framebuffer probe and starve the init of output. Keep it gated.
if [[ "$USE_GPU" -eq 1 ]]; then
qemu_args+=(-device virtio-gpu-pci)
fi
# Optional v6.0 input architecture proof devices.
if [[ "$USE_INPUT" -eq 1 ]]; then
qemu_args+=(-device virtio-keyboard-pci -device virtio-mouse-pci)
fi
if [[ "$USE_IMG" -eq 0 ]]; then
# Boot the live ISO from the virtio CD-ROM-equivalent. We attach via a
# raw virtio-blk rather than ide-cd so the bootloader can find it
# without needing AHCI (still useful for coverage; the kernel sees
# both pathways). snapshot=on is mandatory: without it, kernel writes
# to the live ISO corrupt the build artifact across re-runs.
qemu_args+=(
-drive file="$image",format=raw,if=virtio,snapshot=on,readonly=on
-drive file="$extra",format=raw,if=none,id=drv1,snapshot=on
-device nvme,drive=drv1,serial=NVME_EXTRA
)
else
qemu_args+=(
-drive file="$image",format=raw,if=none,id=drv0,snapshot=on
-device nvme,drive=drv0,serial=NVME_SERIAL
-drive file="$extra",format=raw,if=none,id=drv1,snapshot=on
-device nvme,drive=drv1,serial=NVME_EXTRA
)
fi
qemu_args+=("${kvm_args[@]}")
qemu_args+=("${EXTRA_QEMU_ARGS[@]}")
# Use the `timeout` wrapper to enforce BOOT_TIMEOUT. The `-k 5` sends SIGKILL
# 5 seconds after SIGTERM to avoid a hung QEMU holding /dev/kvm.
timeout_cmd=(timeout --foreground -k 5 "$BOOT_TIMEOUT")
# Run QEMU; capture both stdout and stderr. We split the run from the log
# capture so that a build-side failure (no image, no KVM) is reported
# distinctly from a kernel-side boot failure.
"${timeout_cmd[@]}" "${qemu_args[@]}" >"$log_path" 2>&1 || true
# Always print a footer with the captured log size and last lines so a
# human reader can spot tail-time crashes without re-opening the file.
log_size=$(wc -c <"$log_path" 2>/dev/null || echo 0)
echo
echo "=== Boot test finished ==="
echo "Log: $log_path"
echo "Size: ${log_size} bytes"
echo
echo "--- Last 20 lines of captured log ---"
tail -20 "$log_path" 2>/dev/null || echo "(log unavailable)"
echo "--- End of log ---"
# Don't fail the script on boot failure — the log is the deliverable.
exit 0