#!/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