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:
Executable
+297
@@ -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
|
||||
Reference in New Issue
Block a user