Screenshots stay tracked in git, same as before -- just organized so verification images are easy to find per architecture as Console work progresses across all three ISAs. scripts/qemu_screenshot.sh (amd64-only) now writes into fb/amd64/.
169 lines
6.4 KiB
Bash
Executable File
169 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# qemu_screenshot.sh — Boot LithosAnanke under QEMU, wait for ok>, capture framebuffer.
|
|
#
|
|
# Usage:
|
|
# scripts/qemu_screenshot.sh <LOADER_EFI> <OVMF_CODE> <OVMF_VARS_RO> <BUILD_DIR> <LOG_DIR>
|
|
# scripts/qemu_screenshot.sh <LOADER_EFI> <OVMF_CODE> <OVMF_VARS_RO> <BUILD_DIR> <LOG_DIR> <PRE_BUILT_ISO>
|
|
#
|
|
# If PRE_BUILT_ISO is supplied the ISO build step is skipped (used by the `qemu`
|
|
# Makefile target which already built the ISO during the serial acceptance run).
|
|
#
|
|
# Outputs:
|
|
# <BUILD_DIR>/screenshot.ppm — raw PPM from QEMU screendump
|
|
# <LOG_DIR>/qemu-screenshot-YYYYMMDD-HHMMSS.log — serial log
|
|
# fb/amd64/qemu-screenshot-YYYYMMDD-HHMMSS.png — PNG (tracked in git, not build/)
|
|
# (amd64-only script; fb/ has one subdirectory per ISA by convention)
|
|
#
|
|
# Requires: qemu-system-x86_64, socat, xorriso, mtools, ovmf, imagemagick
|
|
|
|
set -e
|
|
|
|
LOADER_EFI="$1"
|
|
OVMF_CODE="$2"
|
|
OVMF_VARS_RO="$3"
|
|
BUILD_DIR="$4"
|
|
LOG_DIR="$5"
|
|
PRE_BUILT_ISO="$6" # optional: skip ISO build if supplied
|
|
|
|
if [ -z "$LOADER_EFI" ] || [ -z "$OVMF_CODE" ] || [ -z "$OVMF_VARS_RO" ] \
|
|
|| [ -z "$BUILD_DIR" ] || [ -z "$LOG_DIR" ]; then
|
|
echo "Usage: $0 <LOADER_EFI> <OVMF_CODE> <OVMF_VARS_RO> <BUILD_DIR> <LOG_DIR> [PRE_BUILT_ISO]"
|
|
exit 1
|
|
fi
|
|
|
|
for dep in qemu-system-x86_64 socat xorriso mformat; do
|
|
if ! command -v "$dep" >/dev/null 2>&1; then
|
|
echo "Error: $dep not found. Install: apt-get install ${dep%%_*}"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Build ISO — skipped when a pre-built ISO is provided (e.g. from `qemu` target)
|
|
# --------------------------------------------------------------------------
|
|
if [ -n "$PRE_BUILT_ISO" ]; then
|
|
QEMU_ISO="$PRE_BUILT_ISO"
|
|
echo " Using pre-built ISO: ${QEMU_ISO}"
|
|
else
|
|
echo " Building ISO for screendump..."
|
|
mkdir -p "${BUILD_DIR}/iso"
|
|
dd if=/dev/zero of="${BUILD_DIR}/iso/efi.img" bs=512 count=8192 2>/dev/null
|
|
mformat -i "${BUILD_DIR}/iso/efi.img" ::
|
|
mmd -i "${BUILD_DIR}/iso/efi.img" ::/EFI
|
|
mmd -i "${BUILD_DIR}/iso/efi.img" ::/EFI/BOOT
|
|
mcopy -i "${BUILD_DIR}/iso/efi.img" "${LOADER_EFI}" ::/EFI/BOOT/BOOTX64.EFI
|
|
|
|
xorriso -as mkisofs -r -J \
|
|
-e efi.img -no-emul-boot \
|
|
-o "${BUILD_DIR}/starkernel.iso" "${BUILD_DIR}/iso" 2>&1 | grep -v "^xorriso" || true
|
|
|
|
QEMU_ISO="${BUILD_DIR}/starkernel.iso"
|
|
fi
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Setup paths
|
|
# --------------------------------------------------------------------------
|
|
FB_DIR="fb/amd64"
|
|
mkdir -p "${LOG_DIR}" "${BUILD_DIR}" "${FB_DIR}"
|
|
|
|
TS=$(date +%Y%m%d-%H%M%S)
|
|
SERIAL_LOG="${LOG_DIR}/qemu-screenshot-${TS}.log"
|
|
VARS_COPY="${BUILD_DIR}/OVMF_VARS_screenshot.fd"
|
|
MONITOR_SOCK="${BUILD_DIR}/qemu-monitor-${TS}.sock"
|
|
PPM_OUT="${BUILD_DIR}/screenshot.ppm"
|
|
PNG_LOG="${FB_DIR}/qemu-screenshot-${TS}.png" # committed artifact
|
|
QEMU_PID_FILE="${BUILD_DIR}/qemu-screenshot.pid"
|
|
|
|
cp "${OVMF_VARS_RO}" "${VARS_COPY}"
|
|
|
|
echo " Serial log : ${SERIAL_LOG}"
|
|
echo " Monitor : ${MONITOR_SOCK}"
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Launch QEMU in background with:
|
|
# -serial file:<log> — capture text output
|
|
# -monitor unix:<sock>,server,nowait — HMP monitor on UNIX socket
|
|
# -display none — headless; VGA surface still exists
|
|
# (VGA is the default device on q35; OVMF GOP maps to it)
|
|
# --------------------------------------------------------------------------
|
|
qemu-system-x86_64 \
|
|
-machine q35,accel=tcg \
|
|
-cpu qemu64 \
|
|
-m 1024 \
|
|
-drive if=pflash,format=raw,readonly=on,file="${OVMF_CODE}" \
|
|
-drive if=pflash,format=raw,file="${VARS_COPY}" \
|
|
-cdrom "${QEMU_ISO}" -boot d \
|
|
-serial file:"${SERIAL_LOG}" \
|
|
-monitor unix:"${MONITOR_SOCK}",server,nowait \
|
|
-display none \
|
|
-no-reboot \
|
|
&
|
|
QEMU_PID=$!
|
|
echo "$QEMU_PID" > "${QEMU_PID_FILE}"
|
|
echo " QEMU PID : ${QEMU_PID}"
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Wait for ok> prompt in serial log (up to 120 s)
|
|
# --------------------------------------------------------------------------
|
|
echo " Waiting for ok> prompt..."
|
|
DEADLINE=$(( $(date +%s) + 1800 ))
|
|
while true; do
|
|
if [ "$(date +%s)" -ge "$DEADLINE" ]; then
|
|
echo " TIMEOUT waiting for ok> — killing QEMU"
|
|
kill "$QEMU_PID" 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
if grep -q "ok>" "${SERIAL_LOG}" 2>/dev/null; then
|
|
echo " ok> detected!"
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
# Give the framebuffer a moment to fully paint before capturing
|
|
sleep 2
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Issue screendump via HMP monitor socket
|
|
# --------------------------------------------------------------------------
|
|
echo "screendump ${PPM_OUT}" | socat - "UNIX-CONNECT:${MONITOR_SOCK}" 2>/dev/null || true
|
|
sleep 1
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Terminate QEMU
|
|
# --------------------------------------------------------------------------
|
|
echo "quit" | socat - "UNIX-CONNECT:${MONITOR_SOCK}" 2>/dev/null || true
|
|
sleep 1
|
|
kill "$QEMU_PID" 2>/dev/null || true
|
|
wait "$QEMU_PID" 2>/dev/null || true
|
|
rm -f "${QEMU_PID_FILE}" "${MONITOR_SOCK}" "${VARS_COPY}"
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Convert PPM → PNG
|
|
# --------------------------------------------------------------------------
|
|
if [ -f "${PPM_OUT}" ]; then
|
|
echo " PPM : ${PPM_OUT} ($(wc -c < "${PPM_OUT}") bytes)"
|
|
if command -v convert >/dev/null 2>&1; then
|
|
convert "${PPM_OUT}" "${PNG_LOG}"
|
|
rm -f "${PPM_OUT}"
|
|
echo " PNG : ${PNG_LOG}"
|
|
elif command -v python3 >/dev/null 2>&1 && python3 -c "import PIL" >/dev/null 2>&1; then
|
|
python3 -c "from PIL import Image; Image.open('${PPM_OUT}').save('${PNG_LOG}')"
|
|
rm -f "${PPM_OUT}"
|
|
echo " PNG : ${PNG_LOG} (via python3/PIL)"
|
|
else
|
|
echo " WARN: no PPM->PNG converter found (imagemagick 'convert' or python3+PIL) — PPM only"
|
|
PNG_LOG="${FB_DIR}/$(basename "${PNG_LOG}" .png).ppm"
|
|
mv "${PPM_OUT}" "${PNG_LOG}"
|
|
fi
|
|
echo ""
|
|
echo "=== Screendump complete ==="
|
|
echo " Serial log : ${SERIAL_LOG}"
|
|
echo " Screenshot : ${PNG_LOG}"
|
|
else
|
|
echo " ERROR: PPM not written — screendump may have failed"
|
|
echo " Serial log: ${SERIAL_LOG}"
|
|
cat "${SERIAL_LOG}" 2>/dev/null | tail -20
|
|
exit 1
|
|
fi
|