diff --git a/local/scripts/test-redbear-full-qemu.sh b/local/scripts/test-redbear-full-qemu.sh new file mode 100755 index 0000000000..ecb136a4a1 --- /dev/null +++ b/local/scripts/test-redbear-full-qemu.sh @@ -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