4325590686
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.
297 lines
11 KiB
Bash
Executable File
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"
|