Files
hiperiso/host/initramfs/qemu_launch.sh
T
vasilito 4325590686 Add host kernel config and initramfs scripts with P0-P3 hardware inventory
Includes: init (PID 1), hiperiso-lib.sh, qemu_launch.sh, hw_collect.sh,

kvm_check.sh, fallback_boot.sh, log_flush.sh, conf_replace.sh, make_floppy.sh.

13-phase boot timing, 18 QEMU HMP commands, network pcap capture.
2026-06-30 14:30:39 +03:00

297 lines
11 KiB
Bash
Executable File

#!/bin/sh
# ============================================================
# qemu_launch.sh -- Build QEMU arguments and launch guest VM
#
# Reads from environment (exported by init):
# ISO_PATH -- absolute path to the ISO on mounted USB
# LOG_DIR -- absolute path to the log directory
# OVMF_FULL_PATH -- absolute path to OVMF firmware on ESP
# EFI_MOUNT -- ESP mount point
# HIPERISO_TRACE_LEVEL -- standard|detailed|full|none
# HIPERISO_GUEST_RAM -- guest RAM in MB
# HIPERISO_GUEST_CPUS -- guest vCPU count
# HIPERISO_DISPLAY -- none|gtk|vnc
# HIPERISO_VGA -- none|std|virtio
# KVM_AVAILABLE -- 1 if KVM detected, 0 otherwise
#
# Exit codes:
# 0 QEMU ran and exited cleanly
# 2 ISO file not found
# 3 OVMF firmware not found
# 4 Insufficient RAM
# 127 QEMU binary missing
# other QEMU's own exit code
# ============================================================
. /hiperiso-lib.sh
QEMU_BIN="/usr/bin/qemu-system-x86_64"
ISO_PATH="${ISO_PATH:?ISO_PATH not set}"
LOG_DIR="${LOG_DIR:?LOG_DIR not set}"
trap 'rm -f /tmp/hw_mon.in /tmp/hw_mon.out 2>/dev/null; for _pf in /tmp/hw_mon_reader.pid /tmp/hw_mon_writer.pid; do [ -f "$_pf" ] && kill "$(cat "$_pf" 2>/dev/null)" 2>/dev/null && rm -f "$_pf"; done' INT TERM
# ── Validate QEMU binary ─────────────────────────────────────
if [ ! -x "$QEMU_BIN" ]; then
hiperiso_log "FATAL: QEMU binary not found at $QEMU_BIN"
exit 127
fi
# ── Validate ISO exists ──────────────────────────────────────
if [ ! -f "$ISO_PATH" ]; then
hiperiso_log "FATAL: ISO file not found: $ISO_PATH"
exit 2
fi
# ── Resolve OVMF firmware ────────────────────────────────────
OVMF_FILE=""
for _candidate in \
"${OVMF_FULL_PATH:-}" \
"${EFI_MOUNT:-/mnt/efi}${OVMF_PATH}" \
"/EFI/hiperiso/OVMF.fd" \
"/usr/share/hiperiso/OVMF.fd" \
"/usr/share/OVMF/OVMF_CODE.fd"; do
if [ -n "$_candidate" ] && [ -f "$_candidate" ]; then
OVMF_FILE="$_candidate"
break
fi
done
if [ -z "$OVMF_FILE" ]; then
hiperiso_log "FATAL: OVMF firmware not found"
hiperiso_log " Searched ESP, initramfs, /usr/share/hiperiso/, /usr/share/OVMF/"
exit 3
fi
hiperiso_log "Using OVMF firmware: $OVMF_FILE"
# ── Plugin: Secure Boot OVMF ─────────────────────────────────
if [ "${HIPERISO_SECURE_BOOT:-0}" = "1" ]; then
_ovmf_secure="${EFI_MOUNT:-/mnt/efi}/EFI/hiperiso/OVMF_SECURE.fd"
if [ -f "$_ovmf_secure" ]; then
hiperiso_log "Plugin: Secure Boot requested, switching firmware to $_ovmf_secure"
OVMF_FILE="$_ovmf_secure"
else
hiperiso_log "WARNING: Secure Boot requested but firmware not found: $_ovmf_secure"
fi
fi
# ── Resolve trace events file ────────────────────────────────
_trace_level="${HIPERISO_TRACE_LEVEL:-standard}"
TRACE_EVENTS_FILE=""
if [ "$_trace_level" != "none" ]; then
for _trace_file in \
"${EFI_MOUNT:-/mnt/efi}${TRACE_EVENTS_DIR}/trace-${_trace_level}.events" \
"/EFI/hiperiso/trace/trace-${_trace_level}.events"; do
if [ -f "$_trace_file" ]; then
TRACE_EVENTS_FILE="$_trace_file"
hiperiso_log "Trace: level=${_trace_level} events=${TRACE_EVENTS_FILE}"
break
fi
done
if [ -z "$TRACE_EVENTS_FILE" ]; then
hiperiso_log "WARNING: trace events file not found for level=${_trace_level}"
hiperiso_log " Tracing disabled for this session"
fi
fi
# ── Verify sufficient RAM ────────────────────────────────────
_guest_ram="${HIPERISO_GUEST_RAM:-2048}"
if ! hiperiso_check_ram "$_guest_ram"; then
hiperiso_log "FATAL: Insufficient RAM for guest"
hiperiso_log " Requested ${_guest_ram}MB guest + 512MB host overhead"
hiperiso_log " Available: $(awk '/^MemTotal:/ {printf "%d", $2/1024}' /proc/meminfo 2>/dev/null)MB"
exit 4
fi
# ── Plugin: memdisk boot mode ────────────────────────────────
if [ "${HIPERISO_BOOT_MODE:-normal}" = "memdisk" ]; then
hiperiso_log "Plugin: memdisk boot mode -- loading ISO into RAM"
cat "$ISO_PATH" > /tmp/iso_memdisk
ISO_PATH="/tmp/iso_memdisk"
fi
# ── Collect host hardware inventory ──────────────────────────
hiperiso_timing_mark "hw_collect_pre"
/hw_collect.sh pre
# ── Build QEMU argument list ─────────────────────────────────
hiperiso_timing_mark "qemu_args_built"
_guest_cpus="${HIPERISO_GUEST_CPUS:-2}"
_display="${HIPERISO_DISPLAY:-none}"
_vga="${HIPERISO_VGA:-std}"
if [ "${KVM_AVAILABLE:-1}" = "1" ]; then
_machine="q35,accel=kvm"
_cpu_model="host"
else
_machine="q35"
_cpu_model="qemu64"
hiperiso_log "WARNING: KVM unavailable -- using TCG software emulation (very slow)"
fi
_cpu_arg="$_cpu_model"
if [ -n "${HIPERISO_CPU_FEATURES:-}" ]; then
hiperiso_log "Plugin: CPU features ${HIPERISO_CPU_FEATURES}"
_oldifs="$IFS"
IFS=','
for _feat in $HIPERISO_CPU_FEATURES; do
_cpu_arg="${_cpu_arg},+${_feat}"
done
IFS="$_oldifs"
fi
set -- -machine "$_machine" -cpu "$_cpu_arg"
_OVMF_TMP="/tmp/OVMF.fd"
if ! cp "$OVMF_FILE" "$_OVMF_TMP" 2>/dev/null; then
hiperiso_log "FATAL: Failed to copy OVMF firmware ($OVMF_FILE) to $_OVMF_TMP"
exit 3
fi
set -- "$@" \
-m "$_guest_ram" \
-smp "$_guest_cpus" \
-drive "file=${ISO_PATH},if=none,id=cd0,format=raw,media=cdrom,readonly=on" \
-device ahci,id=ahci0 \
-device ide-cd,drive=cd0,bus=ahci0.0,bootindex=1 \
-drive "if=pflash,format=raw,readonly=on,file=${OVMF_FILE}" \
-drive "if=pflash,format=raw,file=${_OVMF_TMP}" \
-serial "file:${LOG_DIR}/serial.log" \
-display "$_display" \
-vga "$_vga" \
-monitor "unix:${LOG_DIR}/monitor.sock,server,nowait" \
-nodefaults \
-no-reboot
if [ -n "$TRACE_EVENTS_FILE" ]; then
set -- "$@" \
-trace "events=${TRACE_EVENTS_FILE},file=${LOG_DIR}/trace.bin"
fi
# ── Plugin: persistence .dat ─────────────────────────────────
if [ -n "${HIPERISO_PERSISTENCE:-}" ]; then
hiperiso_log "Plugin: persistence file ${HIPERISO_PERSISTENCE}"
set -- "$@" \
-drive "file=${DATA_MOUNT:-}${HIPERISO_PERSISTENCE},if=none,id=persist0,format=raw" \
-device "virtio-blk-pci,drive=persist0"
fi
# ── Plugin: auto-install script ──────────────────────────────
_floppy_drives=""
_floppy_devices=""
if [ -n "${HIPERISO_AUTO_INSTALL:-}" ]; then
hiperiso_log "Plugin: auto-install script ${HIPERISO_AUTO_INSTALL}"
/make_floppy.sh /tmp/auto_install.img "${DATA_MOUNT:-}${HIPERISO_AUTO_INSTALL}"
_floppy_drives="$_floppy_drives -drive file=/tmp/auto_install.img,if=none,id=floppy0,format=raw"
_floppy_devices="driveA=floppy0"
fi
# ── Plugin: Driver Update Disk ───────────────────────────────
if [ -n "${HIPERISO_DUD:-}" ]; then
hiperiso_log "Plugin: Driver Update Disk ${HIPERISO_DUD}"
set -- "$@" \
-drive "file=${DATA_MOUNT:-}${HIPERISO_DUD},if=none,id=dud0,format=raw,media=cdrom,readonly=on" \
-device "ide-cd,drive=dud0,bus=ahci0.1"
fi
# ── Plugin: injection archive ────────────────────────────────
if [ -n "${HIPERISO_INJECTION:-}" ]; then
hiperiso_log "Plugin: injection archive ${HIPERISO_INJECTION}"
mkdir -p /tmp/injection
tar xzf "${DATA_MOUNT:-}${HIPERISO_INJECTION}" -C /tmp/injection
/make_floppy.sh /tmp/injection.img /tmp/injection/*
if [ -n "$_floppy_devices" ]; then
_floppy_drives="$_floppy_drives -drive file=/tmp/injection.img,if=none,id=floppy1,format=raw"
_floppy_devices="$_floppy_devices,driveB=floppy1"
else
_floppy_drives="$_floppy_drives -drive file=/tmp/injection.img,if=none,id=floppy1,format=raw"
_floppy_devices="driveA=floppy1"
fi
fi
# Single isa-fdc controller for all floppies (QEMU allows only one)
if [ -n "$_floppy_devices" ]; then
set -- "$@" $_floppy_drives -device "isa-fdc,$_floppy_devices"
fi
# ── Plugin: virtual TPM ──────────────────────────────────────
if [ "${HIPERISO_TPM:-0}" = "1" ]; then
if command -v swtpm >/dev/null 2>&1; then
hiperiso_log "Plugin: virtual TPM (swtpm) starting"
mkdir -p /tmp/tpmstate
swtpm socket --tpmstate dir=/tmp/tpmstate \
--ctrl type=unixio,path=/tmp/swtpm.sock \
--tpm2 --daemon
set -- "$@" \
-chardev "socket,id=tpm0,path=/tmp/swtpm.sock" \
-tpmdev "emulator,id=tpm0,chardev=tpm0" \
-device "tpm-crb,tpmdev=tpm0"
else
hiperiso_log "WARNING: TPM requested but swtpm binary not found"
fi
fi
# ── Plugin: network packet capture ───────────────────────────
if [ "${HIPERISO_NET_DUMP:-0}" = "1" ]; then
hiperiso_log "Plugin: network pcap capture enabled"
set -- "$@" \
-netdev "user,id=net0" \
-device "virtio-net-pci,netdev=net0" \
-object "filter-dump,id=netdump,netdev=net0,file=${LOG_DIR}/network.pcap"
fi
# ── Hardware inventory: monitor FIFO + pipe chardev ──────────
# Sets up /tmp/hw_mon.{in,out} FIFOs and launches background
# reader/writer processes. The pipe chardev lets us send HMP
# commands (info qtree, info pci, etc.) to QEMU while it runs.
hiperiso_timing_mark "hw_monitor_setup"
/hw_collect.sh monitor
set -- "$@" \
-chardev "pipe,id=hwmon,path=/tmp/hw_mon" \
-mon "chardev=hwmon,mode=readline"
# ── Save the exact command line ──────────────────────────────
{
printf '%s' "$QEMU_BIN"
for _arg in "$@"; do
printf ' %s' "$_arg"
done
printf '\n'
} > "${LOG_DIR}/qemu.cmdline"
hiperiso_log "QEMU command line saved to ${LOG_DIR}/qemu.cmdline"
# ── Launch QEMU ──────────────────────────────────────────────
hiperiso_timing_mark "qemu_started"
hiperiso_log "Starting QEMU..."
hiperiso_log " Guest: ${_guest_ram}MB RAM, ${_guest_cpus} vCPU(s)"
hiperiso_log " ISO: $ISO_PATH"
hiperiso_log " Video: display=${_display} vga=${_vga}"
"$QEMU_BIN" "$@"
_qemu_exit=$?
hiperiso_timing_mark "qemu_exited"
# ── Parse QEMU hardware inventory ────────────────────────────
/hw_collect.sh post
hiperiso_log "QEMU exited with code: $_qemu_exit"
case "$_qemu_exit" in
0)
hiperiso_log "Guest VM shut down cleanly"
;;
1)
hiperiso_log "QEMU reported an error (see serial.log for details)"
;;
*)
hiperiso_log "QEMU exited with non-zero code: $_qemu_exit"
;;
esac
exit "$_qemu_exit"