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.
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
# Build artifacts
|
||||
build/
|
||||
grub2/
|
||||
/build/
|
||||
/grub2/
|
||||
*.o
|
||||
*.obj
|
||||
*.mod
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
# conf_replace.sh -- Create modified ISO copy with replaced config files
|
||||
# Parses HIPERISO_CONF_REPLACE="org=/path;new=/path" from environment
|
||||
# If set, creates /tmp/modified.iso and prints its path on stdout.
|
||||
# Called by init as a subprocess: _new=$(sh /conf_replace.sh)
|
||||
# POSIX sh compatible
|
||||
|
||||
. /hiperiso-lib.sh
|
||||
|
||||
if [ -z "${HIPERISO_CONF_REPLACE:-}" ]; then
|
||||
# No conf_replace configured -- output nothing, exit clean
|
||||
return 0 2>/dev/null || exit 0
|
||||
fi
|
||||
|
||||
_org=""
|
||||
_new=""
|
||||
OLDIFS="$IFS"
|
||||
IFS=';'
|
||||
for _pair in $HIPERISO_CONF_REPLACE; do
|
||||
case "$_pair" in
|
||||
org=*) _org="${_pair#org=}" ;;
|
||||
new=*) _new="${_pair#new=}" ;;
|
||||
esac
|
||||
done
|
||||
IFS="$OLDIFS"
|
||||
|
||||
if [ -z "$_org" ] || [ -z "$_new" ]; then
|
||||
hiperiso_log "conf_replace: malformed config, skipping"
|
||||
return 0 2>/dev/null || exit 0
|
||||
fi
|
||||
|
||||
_new_abs="${DATA_MOUNT}${_new}"
|
||||
if [ ! -f "$_new_abs" ]; then
|
||||
hiperiso_log "conf_replace: replacement file not found: $_new_abs"
|
||||
return 0 2>/dev/null || exit 0
|
||||
fi
|
||||
|
||||
if ! command -v xorriso >/dev/null 2>&1; then
|
||||
hiperiso_log "conf_replace: xorriso not available, skipping ISO modification"
|
||||
return 0 2>/dev/null || exit 0
|
||||
fi
|
||||
|
||||
_modified_iso="/tmp/modified.iso"
|
||||
hiperiso_log "conf_replace: creating modified ISO copy"
|
||||
|
||||
if xorriso -indev "$ISO_PATH" \
|
||||
-outdev "$_modified_iso" \
|
||||
-boot_image any keep \
|
||||
-rm "$_org" \
|
||||
-add "$_new_abs"="$_org" \
|
||||
2>/dev/null; then
|
||||
|
||||
if [ -f "$_modified_iso" ]; then
|
||||
hiperiso_log "conf_replace: modified ISO at $_modified_iso"
|
||||
# Output the path for the calling process to capture
|
||||
printf '%s' "$_modified_iso"
|
||||
return 0 2>/dev/null || exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
hiperiso_log "conf_replace: xorriso failed, using original ISO"
|
||||
rm -f "$_modified_iso" 2>/dev/null || true
|
||||
return 0 2>/dev/null || exit 0
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/bin/sh
|
||||
# ============================================================
|
||||
# fallback_boot.sh -- Structured failure capture when KVM absent
|
||||
#
|
||||
# Called by init when hardware virtualization is unavailable
|
||||
# and hiperiso_fallback is not "1". Writes structured session
|
||||
# metadata (session.json, status.json, FAILURE.txt) so the boot
|
||||
# attempt is captured even though no QEMU guest was launched,
|
||||
# then warns the user and offers to reboot.
|
||||
# ============================================================
|
||||
|
||||
. /hiperiso-lib.sh
|
||||
|
||||
# ── Capture structured failure data ───────────────────────────
|
||||
if [ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ]; then
|
||||
hiperiso_log "FALLBACK: KVM unavailable -- capturing failure session"
|
||||
|
||||
hiperiso_write_session_meta "failed"
|
||||
hiperiso_write_status "failed" "fallback_no_kvm"
|
||||
|
||||
{
|
||||
printf '========================================\n'
|
||||
printf ' hiperiso FAILURE: KVM Unavailable\n'
|
||||
printf '========================================\n\n'
|
||||
printf 'ISO: %s\n' "${HIPERISO_ISO_PATH:-unknown}"
|
||||
printf 'Session: %s\n' "$(hiperiso_session_id)"
|
||||
printf 'Timestamp: %s\n' "$(hiperiso_utc_iso)"
|
||||
printf 'Reason: KVM not detected (VT-x/AMD-V unavailable)\n\n'
|
||||
printf 'No QEMU guest was launched. No serial.log or\n'
|
||||
printf 'trace.bin was produced for this session.\n'
|
||||
printf '========================================\n'
|
||||
} > "${LOG_DIR}/FAILURE.txt"
|
||||
sync "${LOG_DIR}/FAILURE.txt" 2>/dev/null || sync
|
||||
|
||||
hiperiso_run_report
|
||||
# Finalize so the failed session gets a canonical manifest and an
|
||||
# entry in the global JSONL index (failed sessions are indexed too).
|
||||
hiperiso_finalize_session "failed" "" "fallback_no_kvm"
|
||||
fi
|
||||
|
||||
# Clear the terminal
|
||||
printf '\033[2J\033[H'
|
||||
|
||||
printf '\n'
|
||||
printf '======================================================\n'
|
||||
printf ' WARNING: Hardware Virtualization (KVM) Unavailable \n'
|
||||
printf '======================================================\n'
|
||||
printf '\n'
|
||||
printf ' hiperiso requires KVM (Intel VT-x or AMD-V) to run\n'
|
||||
printf ' an ISO as a guest virtual machine.\n'
|
||||
printf '\n'
|
||||
printf ' KVM was NOT detected on this system, so this ISO\n'
|
||||
printf ' cannot be booted in hypervisor mode.\n'
|
||||
printf '\n'
|
||||
printf ' Possible reasons:\n'
|
||||
printf ' 1. The CPU does not support VT-x / AMD-V\n'
|
||||
printf ' 2. Virtualization is disabled in BIOS/UEFI\n'
|
||||
printf ' 3. KVM kernel modules failed to load\n'
|
||||
printf '\n'
|
||||
printf ' How to proceed:\n'
|
||||
printf ' - Reboot, enable VT-x/AMD-V in BIOS/UEFI, retry\n'
|
||||
printf ' - Use a direct-boot tool (e.g. Rufus, balenaEtcher) for this ISO\n'
|
||||
printf ' - Reboot with hiperiso_fallback=1 on the kernel\n'
|
||||
printf ' command line to force TCG emulation (very slow)\n'
|
||||
printf '\n'
|
||||
if [ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ]; then
|
||||
printf ' Failure session captured: %s\n' "$LOG_DIR"
|
||||
printf '\n'
|
||||
fi
|
||||
printf '======================================================\n'
|
||||
printf '\n'
|
||||
|
||||
printf ' Press ENTER to reboot to the boot menu,\n'
|
||||
printf ' or type "shell" for a recovery shell: '
|
||||
|
||||
read -r _choice
|
||||
case "$_choice" in
|
||||
shell|sh)
|
||||
printf '\nDropping to recovery shell...\n'
|
||||
exec /bin/sh
|
||||
;;
|
||||
*)
|
||||
printf '\nRebooting...\n'
|
||||
sync
|
||||
if command -v reboot >/dev/null 2>&1; then
|
||||
reboot -f
|
||||
else
|
||||
# Fallback: magic SysRq
|
||||
printf 'b' > /proc/sysrq-trigger 2>/dev/null
|
||||
fi
|
||||
# Should not reach here
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
Executable
+706
@@ -0,0 +1,706 @@
|
||||
#!/bin/sh
|
||||
# ============================================================
|
||||
# hiperiso-lib.sh -- Shared functions for hiperiso initramfs
|
||||
# POSIX sh compatible (dash/ash/busybox sh)
|
||||
# ============================================================
|
||||
|
||||
# ── Mount points (from INTERFACES.sh) ────────────────────────
|
||||
EFI_MOUNT="/mnt/efi"
|
||||
DATA_MOUNT="/mnt/usb"
|
||||
|
||||
# ── Paths on EFI partition (from INTERFACES.sh) ──────────────
|
||||
EFI_PAYLOAD_DIR="/EFI/hiperiso"
|
||||
KERNEL_PATH="${EFI_PAYLOAD_DIR}/vmlinuz"
|
||||
INITRAMFS_PATH="${EFI_PAYLOAD_DIR}/initramfs.cpio.gz"
|
||||
OVMF_PATH="${EFI_PAYLOAD_DIR}/OVMF.fd"
|
||||
TRACE_EVENTS_DIR="${EFI_PAYLOAD_DIR}/trace"
|
||||
|
||||
# ── hiperiso_strip_quotes() ──────────────────────────────────
|
||||
# Remove surrounding double or single quotes from a string
|
||||
hiperiso_strip_quotes() {
|
||||
_s="$1"
|
||||
_s="${_s#\"}" # strip leading double-quote
|
||||
_s="${_s%\"}" # strip trailing double-quote
|
||||
_s="${_s#\'}" # strip leading single-quote
|
||||
_s="${_s%\'}" # strip trailing single-quote
|
||||
printf '%s' "$_s"
|
||||
}
|
||||
|
||||
# ── hiperiso_parse_cmdline() ─────────────────────────────────
|
||||
# Extract hiperiso_* parameters from /proc/cmdline.
|
||||
# Sets and exports: HIPERISO_ISO_PATH, HIPERISO_LOG_DIR,
|
||||
# HIPERISO_TRACE_LEVEL, HIPERISO_GUEST_RAM, HIPERISO_GUEST_CPUS,
|
||||
# HIPERISO_DISPLAY, HIPERISO_VGA, HIPERISO_FALLBACK
|
||||
hiperiso_parse_cmdline() {
|
||||
HIPERISO_ISO_PATH=""
|
||||
HIPERISO_LOG_DIR=""
|
||||
HIPERISO_TRACE_LEVEL=""
|
||||
HIPERISO_GUEST_RAM=""
|
||||
HIPERISO_GUEST_CPUS=""
|
||||
HIPERISO_DISPLAY=""
|
||||
HIPERISO_VGA=""
|
||||
HIPERISO_FALLBACK=""
|
||||
HIPERISO_AUTO_INSTALL=""
|
||||
HIPERISO_PERSISTENCE=""
|
||||
HIPERISO_DUD=""
|
||||
HIPERISO_INJECTION=""
|
||||
HIPERISO_CONF_REPLACE=""
|
||||
HIPERISO_SECURE_BOOT=""
|
||||
HIPERISO_TPM=""
|
||||
HIPERISO_CPU_FEATURES=""
|
||||
HIPERISO_BOOT_MODE=""
|
||||
HIPERISO_NET_DUMP=""
|
||||
|
||||
for _token in $(cat /proc/cmdline 2>/dev/null); do
|
||||
case "$_token" in
|
||||
hiperiso_iso=*) HIPERISO_ISO_PATH="${_token#hiperiso_iso=}" ;;
|
||||
hiperiso_log=*) HIPERISO_LOG_DIR="${_token#hiperiso_log=}" ;;
|
||||
hiperiso_trace_level=*) HIPERISO_TRACE_LEVEL="${_token#hiperiso_trace_level=}" ;;
|
||||
hiperiso_ram=*) HIPERISO_GUEST_RAM="${_token#hiperiso_ram=}" ;;
|
||||
hiperiso_cpus=*) HIPERISO_GUEST_CPUS="${_token#hiperiso_cpus=}" ;;
|
||||
hiperiso_display=*) HIPERISO_DISPLAY="${_token#hiperiso_display=}" ;;
|
||||
hiperiso_vga=*) HIPERISO_VGA="${_token#hiperiso_vga=}" ;;
|
||||
hiperiso_fallback=*) HIPERISO_FALLBACK="${_token#hiperiso_fallback=}" ;;
|
||||
hiperiso_auto_install=*) HIPERISO_AUTO_INSTALL="${_token#hiperiso_auto_install=}" ;;
|
||||
hiperiso_persistence=*) HIPERISO_PERSISTENCE="${_token#hiperiso_persistence=}" ;;
|
||||
hiperiso_dud=*) HIPERISO_DUD="${_token#hiperiso_dud=}" ;;
|
||||
hiperiso_injection=*) HIPERISO_INJECTION="${_token#hiperiso_injection=}" ;;
|
||||
hiperiso_conf_replace=*) HIPERISO_CONF_REPLACE="${_token#hiperiso_conf_replace=}" ;;
|
||||
hiperiso_secure_boot=*) HIPERISO_SECURE_BOOT="${_token#hiperiso_secure_boot=}" ;;
|
||||
hiperiso_tpm=*) HIPERISO_TPM="${_token#hiperiso_tpm=}" ;;
|
||||
hiperiso_cpu_features=*) HIPERISO_CPU_FEATURES="${_token#hiperiso_cpu_features=}" ;;
|
||||
hiperiso_boot_mode=*) HIPERISO_BOOT_MODE="${_token#hiperiso_boot_mode=}" ;;
|
||||
hiperiso_net_dump=*) HIPERISO_NET_DUMP="${_token#hiperiso_net_dump=}" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
HIPERISO_ISO_PATH=$(hiperiso_strip_quotes "$HIPERISO_ISO_PATH")
|
||||
HIPERISO_LOG_DIR=$(hiperiso_strip_quotes "$HIPERISO_LOG_DIR")
|
||||
HIPERISO_TRACE_LEVEL=$(hiperiso_strip_quotes "$HIPERISO_TRACE_LEVEL")
|
||||
HIPERISO_GUEST_RAM=$(hiperiso_strip_quotes "$HIPERISO_GUEST_RAM")
|
||||
HIPERISO_GUEST_CPUS=$(hiperiso_strip_quotes "$HIPERISO_GUEST_CPUS")
|
||||
HIPERISO_DISPLAY=$(hiperiso_strip_quotes "$HIPERISO_DISPLAY")
|
||||
HIPERISO_VGA=$(hiperiso_strip_quotes "$HIPERISO_VGA")
|
||||
HIPERISO_FALLBACK=$(hiperiso_strip_quotes "$HIPERISO_FALLBACK")
|
||||
HIPERISO_AUTO_INSTALL=$(hiperiso_strip_quotes "$HIPERISO_AUTO_INSTALL")
|
||||
HIPERISO_PERSISTENCE=$(hiperiso_strip_quotes "$HIPERISO_PERSISTENCE")
|
||||
HIPERISO_DUD=$(hiperiso_strip_quotes "$HIPERISO_DUD")
|
||||
HIPERISO_INJECTION=$(hiperiso_strip_quotes "$HIPERISO_INJECTION")
|
||||
HIPERISO_CONF_REPLACE=$(hiperiso_strip_quotes "$HIPERISO_CONF_REPLACE")
|
||||
HIPERISO_SECURE_BOOT=$(hiperiso_strip_quotes "$HIPERISO_SECURE_BOOT")
|
||||
HIPERISO_TPM=$(hiperiso_strip_quotes "$HIPERISO_TPM")
|
||||
HIPERISO_CPU_FEATURES=$(hiperiso_strip_quotes "$HIPERISO_CPU_FEATURES")
|
||||
HIPERISO_BOOT_MODE=$(hiperiso_strip_quotes "$HIPERISO_BOOT_MODE")
|
||||
HIPERISO_NET_DUMP=$(hiperiso_strip_quotes "$HIPERISO_NET_DUMP")
|
||||
|
||||
export HIPERISO_ISO_PATH HIPERISO_LOG_DIR HIPERISO_TRACE_LEVEL
|
||||
export HIPERISO_GUEST_RAM HIPERISO_GUEST_CPUS
|
||||
export HIPERISO_DISPLAY HIPERISO_VGA HIPERISO_FALLBACK
|
||||
export HIPERISO_AUTO_INSTALL HIPERISO_PERSISTENCE HIPERISO_DUD
|
||||
export HIPERISO_INJECTION HIPERISO_CONF_REPLACE HIPERISO_SECURE_BOOT
|
||||
export HIPERISO_TPM HIPERISO_CPU_FEATURES HIPERISO_BOOT_MODE
|
||||
export HIPERISO_NET_DUMP
|
||||
}
|
||||
|
||||
# ── hiperiso_find_usb_partition() ────────────────────────────
|
||||
# Scan block devices for the hiperiso USB data partition.
|
||||
# Outputs the device path (e.g. /dev/sdb2) and returns 0,
|
||||
# or outputs nothing and returns 1 if not found.
|
||||
# Strategy: blkid label "HIPERISO" -> known partition 2 devices
|
||||
# -> sysfs removable scan
|
||||
hiperiso_find_usb_partition() {
|
||||
# Strategy 1: blkid by label HIPERISO
|
||||
if command -v blkid >/dev/null 2>&1; then
|
||||
_dev=$(blkid -L HIPERISO 2>/dev/null)
|
||||
if [ -n "$_dev" ] && [ -e "$_dev" ]; then
|
||||
printf '%s' "$_dev"
|
||||
return 0
|
||||
fi
|
||||
_dev=$(blkid -t LABEL=HIPERISO -o device 2>/dev/null | head -n 1)
|
||||
if [ -n "$_dev" ] && [ -e "$_dev" ]; then
|
||||
printf '%s' "$_dev"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Strategy 2: try known partition-1 device nodes (sd[a-f] covers
|
||||
# most USB storage; the data partition is partition 1).
|
||||
for _dev in /dev/sda1 /dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1 /dev/sdf1; do
|
||||
[ -e "$_dev" ] || continue
|
||||
if command -v blkid >/dev/null 2>&1; then
|
||||
# Verify it has a filesystem
|
||||
if blkid "$_dev" >/dev/null 2>&1; then
|
||||
printf '%s' "$_dev"
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
# No blkid available -- accept device if it exists
|
||||
printf '%s' "$_dev"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Strategy 3: scan sysfs for removable block devices with partition 1.
|
||||
# Only returns a device if sysfs marks it as removable to avoid
|
||||
# accidentally selecting the system disk.
|
||||
for _blk in /sys/block/sd[a-z] /sys/block/nvme[0-9]n[0-9] /sys/block/mmcblk[0-9]; do
|
||||
[ -e "$_blk" ] || continue
|
||||
_name=$(basename "$_blk")
|
||||
# NVMe (nvme0n1) and MMC (mmcblk0) use 'p' separator before partition number
|
||||
case "$_name" in
|
||||
nvme*|mmcblk*) _part="/dev/${_name}p1" ;;
|
||||
*) _part="/dev/${_name}1" ;;
|
||||
esac
|
||||
[ -e "$_part" ] || continue
|
||||
_removable=$(cat "${_blk}/removable" 2>/dev/null)
|
||||
if [ "$_removable" = "1" ]; then
|
||||
printf '%s' "$_part"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── hiperiso_log() ───────────────────────────────────────────
|
||||
# Log a message to console and env.txt (if LOG_DIR is set).
|
||||
hiperiso_log() {
|
||||
_msg="$1"
|
||||
printf '[hiperiso] %s\n' "$_msg"
|
||||
if [ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ]; then
|
||||
printf '[hiperiso] %s\n' "$_msg" >> "${LOG_DIR}/env.txt"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── hiperiso_write_env() ─────────────────────────────────────
|
||||
# Write full environment snapshot to ${LOG_DIR}/env.txt.
|
||||
# Caller must set LOG_DIR and have /proc, /sys mounted.
|
||||
hiperiso_write_env() {
|
||||
_envfile="${LOG_DIR}/env.txt"
|
||||
: > "$_envfile"
|
||||
|
||||
{
|
||||
printf '=== hiperiso Environment Snapshot ===\n'
|
||||
printf 'Uptime: %s\n' "$(cut -d' ' -f1 /proc/uptime 2>/dev/null)"
|
||||
printf '\n'
|
||||
printf -- '--- Host Kernel ---\n'
|
||||
cat /proc/version 2>/dev/null
|
||||
printf 'Cmdline: %s\n' "$(cat /proc/cmdline 2>/dev/null)"
|
||||
printf '\n'
|
||||
printf -- '--- Memory ---\n'
|
||||
grep '^MemTotal\|^MemFree\|^MemAvailable\|^SwapTotal' /proc/meminfo 2>/dev/null
|
||||
printf '\n'
|
||||
printf -- '--- CPU ---\n'
|
||||
grep '^model name' /proc/cpuinfo 2>/dev/null | head -n 1
|
||||
grep '^cpu cores' /proc/cpuinfo 2>/dev/null | head -n 1
|
||||
grep '^siblings' /proc/cpuinfo 2>/dev/null | head -n 1
|
||||
printf 'Logical processors: %s\n' \
|
||||
"$(grep -c '^processor' /proc/cpuinfo 2>/dev/null)"
|
||||
if grep -q -E '(vmx|svm)' /proc/cpuinfo 2>/dev/null; then
|
||||
printf 'Hardware virtualization: supported (vmx/svm)\n'
|
||||
else
|
||||
printf 'Hardware virtualization: not detected\n'
|
||||
fi
|
||||
printf '\n'
|
||||
printf -- '--- Block Devices ---\n'
|
||||
ls /dev/sd* 2>/dev/null
|
||||
ls /dev/nvme* 2>/dev/null
|
||||
printf '\n'
|
||||
printf -- '--- hiperiso Configuration ---\n'
|
||||
printf 'ISO path: %s\n' "${HIPERISO_ISO_PATH:-}"
|
||||
printf 'Log dir: %s\n' "${HIPERISO_LOG_DIR:-}"
|
||||
printf 'Trace level: %s\n' "${HIPERISO_TRACE_LEVEL:-standard}"
|
||||
printf 'Guest RAM: %s MB\n' "${HIPERISO_GUEST_RAM:-2048}"
|
||||
printf 'Guest CPUs: %s\n' "${HIPERISO_GUEST_CPUS:-2}"
|
||||
printf 'Display: %s\n' "${HIPERISO_DISPLAY:-none}"
|
||||
printf 'VGA: %s\n' "${HIPERISO_VGA:-std}"
|
||||
printf 'Fallback: %s\n' "${HIPERISO_FALLBACK:-0}"
|
||||
printf 'Data mount: %s\n' "$DATA_MOUNT"
|
||||
printf 'EFI mount: %s\n' "$EFI_MOUNT"
|
||||
printf 'Log dir (abs): %s\n' "$LOG_DIR"
|
||||
printf 'Session ID: %s\n' "$(hiperiso_session_id)"
|
||||
printf '\n'
|
||||
} >> "$_envfile"
|
||||
}
|
||||
|
||||
# ── hiperiso_check_ram() ─────────────────────────────────────
|
||||
# Verify enough RAM for the guest VM.
|
||||
# Args: $1 = guest RAM in MB (default 2048)
|
||||
# Returns: 0 if sufficient, 1 if not
|
||||
hiperiso_check_ram() {
|
||||
_guest_ram="${1:-2048}"
|
||||
_host_overhead=512 # host kernel + initramfs working set
|
||||
_needed=$(( _guest_ram + _host_overhead ))
|
||||
_total_kb=$(awk '/^MemTotal:/ { print $2 }' /proc/meminfo 2>/dev/null)
|
||||
if [ -z "$_total_kb" ]; then
|
||||
return 0 # cannot determine -- assume OK
|
||||
fi
|
||||
_total_mb=$(( _total_kb / 1024 ))
|
||||
if [ "$_total_mb" -lt "$_needed" ]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── hiperiso_utc_stamp() ──────────────────────────────────────
|
||||
# Output a compact UTC timestamp suitable for directory names:
|
||||
# YYYYMMDDTHHMMSSZ
|
||||
# Falls back to uptime-based identifier if date(1) is unavailable.
|
||||
hiperiso_utc_stamp() {
|
||||
_ts=$(date -u +%Y%m%dT%H%M%SZ 2>/dev/null)
|
||||
if [ -z "$_ts" ]; then
|
||||
_up=$(cut -d. -f1 /proc/uptime 2>/dev/null)
|
||||
_ts="boot${_up:-0}"
|
||||
fi
|
||||
printf '%s' "$_ts"
|
||||
}
|
||||
|
||||
# ── hiperiso_utc_iso() ────────────────────────────────────────
|
||||
# Output ISO 8601 UTC timestamp for JSON metadata:
|
||||
# YYYY-MM-DDTHH:MM:SSZ
|
||||
# Falls back to "unknown" if date(1) is unavailable.
|
||||
hiperiso_utc_iso() {
|
||||
_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)
|
||||
if [ -z "$_ts" ]; then
|
||||
printf '%s' "unknown"
|
||||
else
|
||||
printf '%s' "$_ts"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── hiperiso_json_escape() ────────────────────────────────────
|
||||
# Escape backslashes and double quotes for JSON string safety.
|
||||
# Uses sed if available; falls back to raw value.
|
||||
hiperiso_json_escape() {
|
||||
printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' 2>/dev/null \
|
||||
|| printf '%s' "$1"
|
||||
}
|
||||
|
||||
# ── hiperiso_session_id() ─────────────────────────────────────
|
||||
# Derive the session identifier (directory basename) from the
|
||||
# current HIPERISO_LOG_DIR. E.g. "/hiperiso/logs/ubuntu_Ts/" →
|
||||
# "ubuntu_Ts".
|
||||
hiperiso_session_id() {
|
||||
_sid="${HIPERISO_LOG_DIR%/}"
|
||||
printf '%s' "${_sid##*/}"
|
||||
}
|
||||
|
||||
# ── hiperiso_derive_session_dir() ─────────────────────────────
|
||||
# Given a base log dir from GRUB (e.g. /hiperiso/logs/ubuntu/),
|
||||
# append a UTC timestamp so every boot gets a unique directory.
|
||||
# Input: $1 = base log dir (from hiperiso_log= cmdline param)
|
||||
# Output: /hiperiso/logs/<basename>_<UTC-timestamp>/
|
||||
hiperiso_derive_session_dir() {
|
||||
_base="${1%/}"
|
||||
_ts=$(hiperiso_utc_stamp)
|
||||
_name="${_base##*/}"
|
||||
_parent="${_base%/*}"
|
||||
printf '%s/%s_%s/' "$_parent" "$_name" "$_ts"
|
||||
}
|
||||
|
||||
# ── hiperiso_write_session_meta() ─────────────────────────────
|
||||
# Write session.json into LOG_DIR with structured session metadata
|
||||
# for agent consumption. Caller must have LOG_DIR set and created.
|
||||
hiperiso_write_session_meta() {
|
||||
[ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ] || return 1
|
||||
|
||||
_file="${LOG_DIR}/session.json"
|
||||
_ts=$(hiperiso_utc_iso)
|
||||
_sid=$(hiperiso_session_id)
|
||||
|
||||
# Capture the session start time on first invocation so the
|
||||
# final manifest rewrite can preserve it.
|
||||
if [ -z "${HIPERISO_SESSION_START:-}" ]; then
|
||||
HIPERISO_SESSION_START="$_ts"
|
||||
export HIPERISO_SESSION_START
|
||||
fi
|
||||
|
||||
_iso_basename="${HIPERISO_ISO_PATH##*/}"
|
||||
_iso_esc=$(hiperiso_json_escape "${HIPERISO_ISO_PATH:-}")
|
||||
_log_esc=$(hiperiso_json_escape "${HIPERISO_LOG_DIR%/}")
|
||||
|
||||
{
|
||||
printf '{\n'
|
||||
printf ' "session_id": "%s",\n' "$_sid"
|
||||
printf ' "status": "%s",\n' "${1:-started}"
|
||||
printf ' "iso_path": "%s",\n' "$_iso_esc"
|
||||
printf ' "iso_basename": "%s",\n' "$_iso_basename"
|
||||
printf ' "log_dir": "%s",\n' "$_log_esc"
|
||||
printf ' "start_time_utc": "%s",\n' "$_ts"
|
||||
printf ' "kvm": "%s",\n' "${KVM_STATUS:-unknown}"
|
||||
printf ' "trace_level": "%s",\n' "${HIPERISO_TRACE_LEVEL:-standard}"
|
||||
printf ' "guest_ram_mb": %s,\n' "${HIPERISO_GUEST_RAM:-2048}"
|
||||
printf ' "guest_cpus": %s\n' "${HIPERISO_GUEST_CPUS:-2}"
|
||||
printf '}\n'
|
||||
} > "$_file"
|
||||
sync "$_file" 2>/dev/null || sync
|
||||
}
|
||||
|
||||
# ── hiperiso_write_status() ───────────────────────────────────
|
||||
# Update status.json in LOG_DIR with current session state.
|
||||
# Args: $1 = status (started|running|complete|failed)
|
||||
# $2 = stage (mounted|kvm_ok|tcg_emulation|launching_qemu|
|
||||
# qemu_running|report_generated|session_complete|
|
||||
# fallback_no_kvm|iso_not_found|fatal_error|...)
|
||||
# $3 = qemu_exit_code (optional, integer)
|
||||
hiperiso_write_status() {
|
||||
[ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ] || return 1
|
||||
|
||||
_status="$1"
|
||||
_stage="$2"
|
||||
_qexit="${3:-}"
|
||||
_file="${LOG_DIR}/status.json"
|
||||
_ts=$(hiperiso_utc_iso)
|
||||
_sid=$(hiperiso_session_id)
|
||||
|
||||
{
|
||||
printf '{\n'
|
||||
printf ' "session_id": "%s",\n' "$_sid"
|
||||
printf ' "status": "%s",\n' "$_status"
|
||||
printf ' "stage": "%s",\n' "$_stage"
|
||||
if [ -n "$_qexit" ]; then
|
||||
printf ' "qemu_exit_code": %s,\n' "$_qexit"
|
||||
fi
|
||||
printf ' "timestamp_utc": "%s"\n' "$_ts"
|
||||
printf '}\n'
|
||||
} > "$_file"
|
||||
sync "$_file" 2>/dev/null || sync
|
||||
}
|
||||
|
||||
# ── hiperiso_run_report() ─────────────────────────────────────
|
||||
# Run the hiperiso-log analysis tool on LOG_DIR to auto-generate
|
||||
# report.json and report.txt. Returns 0 on success, 1 if the tool
|
||||
# is missing or fails.
|
||||
hiperiso_run_report() {
|
||||
[ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ] || return 1
|
||||
|
||||
_tool=""
|
||||
for _p in /usr/bin/hiperiso-log /bin/hiperiso-log hiperiso-log; do
|
||||
if command -v "$_p" >/dev/null 2>&1; then
|
||||
_tool="$_p"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$_tool" ]; then
|
||||
hiperiso_log "report: hiperiso-log not found -- skipping report generation"
|
||||
return 1
|
||||
fi
|
||||
|
||||
hiperiso_log "report: generating report.json and report.txt..."
|
||||
if "$_tool" analyze "$LOG_DIR" >>"${LOG_DIR}/report.log" 2>&1; then
|
||||
hiperiso_log "report: generated successfully"
|
||||
return 0
|
||||
else
|
||||
_rc=$?
|
||||
hiperiso_log "report: generation failed (exit $_rc)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── hiperiso_mark_session_complete() ──────────────────────────
|
||||
# Write the session-complete sentinel and do a final sync so agents
|
||||
# can detect that the boot session finished cleanly.
|
||||
hiperiso_mark_session_complete() {
|
||||
[ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ] || return 1
|
||||
|
||||
_ts=$(hiperiso_utc_iso)
|
||||
printf '%s\n' "$_ts" > "${LOG_DIR}/SESSION_COMPLETE"
|
||||
sync "${LOG_DIR}/SESSION_COMPLETE" 2>/dev/null || sync
|
||||
}
|
||||
|
||||
# ── hiperiso_read_analysis_meta() ─────────────────────────────
|
||||
# Parse ${LOG_DIR}/analysis.meta (flat KEY=VALUE written by the
|
||||
# hiperiso-log report tool) into HIPERISO_A_* shell variables.
|
||||
# Missing file leaves all values empty. POSIX sh: no arrays, no
|
||||
# $(<file) -- uses a while-read loop.
|
||||
hiperiso_read_analysis_meta() {
|
||||
HIPERISO_A_BOOT_RESULT=""
|
||||
HIPERISO_A_FAILURE_DOMAIN=""
|
||||
HIPERISO_A_BOOT_STAGE=""
|
||||
HIPERISO_A_REACHED_LOGIN=""
|
||||
HIPERISO_A_KERNEL_PANIC=""
|
||||
HIPERISO_A_ERROR_COUNT=""
|
||||
HIPERISO_A_HAS_SERIAL=""
|
||||
HIPERISO_A_HAS_TRACE=""
|
||||
HIPERISO_A_GRAPHICS_DRIVERS=""
|
||||
HIPERISO_A_GRAPHICS_RES=""
|
||||
HIPERISO_A_DURATION_MS=""
|
||||
|
||||
_meta="${LOG_DIR}/analysis.meta"
|
||||
[ -f "$_meta" ] || return 0
|
||||
|
||||
while IFS='=' read -r _k _v || [ -n "$_k" ]; do
|
||||
[ -n "$_k" ] || continue
|
||||
case "$_k" in
|
||||
BOOT_RESULT) HIPERISO_A_BOOT_RESULT="$_v" ;;
|
||||
FAILURE_DOMAIN) HIPERISO_A_FAILURE_DOMAIN="$_v" ;;
|
||||
BOOT_STAGE_FINAL) HIPERISO_A_BOOT_STAGE="$_v" ;;
|
||||
REACHED_LOGIN) HIPERISO_A_REACHED_LOGIN="$_v" ;;
|
||||
KERNEL_PANIC_COUNT) HIPERISO_A_KERNEL_PANIC="$_v" ;;
|
||||
ERROR_COUNT) HIPERISO_A_ERROR_COUNT="$_v" ;;
|
||||
HAS_SERIAL) HIPERISO_A_HAS_SERIAL="$_v" ;;
|
||||
HAS_TRACE) HIPERISO_A_HAS_TRACE="$_v" ;;
|
||||
GRAPHICS_DRIVERS) HIPERISO_A_GRAPHICS_DRIVERS="$_v" ;;
|
||||
GRAPHICS_RESOLUTION) HIPERISO_A_GRAPHICS_RES="$_v" ;;
|
||||
BOOT_DURATION_MS) HIPERISO_A_DURATION_MS="$_v" ;;
|
||||
esac
|
||||
done < "$_meta"
|
||||
}
|
||||
|
||||
# ── hiperiso_write_final_manifest() ───────────────────────────
|
||||
# Rewrite ${LOG_DIR}/session.json as the canonical final manifest,
|
||||
# folding in end time, qemu exit code, report availability, final
|
||||
# status, and the derived analysis from analysis.meta.
|
||||
# Args: $1=status $2=qemu_exit_code(may be empty) $3=start_utc
|
||||
# $4=end_utc $5=report_available(0|1)
|
||||
hiperiso_write_final_manifest() {
|
||||
[ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ] || return 1
|
||||
|
||||
_status="$1"
|
||||
_qexit="${2:-}"
|
||||
_start_ts="${3:-unknown}"
|
||||
_end_ts="${4:-unknown}"
|
||||
_report_avail="${5:-0}"
|
||||
|
||||
_file="${LOG_DIR}/session.json"
|
||||
_sid=$(hiperiso_session_id)
|
||||
_iso_basename="${HIPERISO_ISO_PATH##*/}"
|
||||
_iso_esc=$(hiperiso_json_escape "${HIPERISO_ISO_PATH:-}")
|
||||
_log_esc=$(hiperiso_json_escape "${HIPERISO_LOG_DIR%/}")
|
||||
_gdrv_esc=$(hiperiso_json_escape "${HIPERISO_A_GRAPHICS_DRIVERS:-}")
|
||||
_gres_esc=$(hiperiso_json_escape "${HIPERISO_A_GRAPHICS_RES:-}")
|
||||
|
||||
# result: prefer the report-derived boot_result; fall back to status.
|
||||
_result="${HIPERISO_A_BOOT_RESULT:-}"
|
||||
[ -n "$_result" ] || _result="$_status"
|
||||
|
||||
{
|
||||
printf '{\n'
|
||||
printf ' "session_id": "%s",\n' "$_sid"
|
||||
printf ' "status": "%s",\n' "$_status"
|
||||
printf ' "result": "%s",\n' "$_result"
|
||||
printf ' "iso_path": "%s",\n' "$_iso_esc"
|
||||
printf ' "iso_basename": "%s",\n' "$_iso_basename"
|
||||
printf ' "log_dir": "%s",\n' "$_log_esc"
|
||||
printf ' "start_time_utc": "%s",\n' "$_start_ts"
|
||||
printf ' "end_time_utc": "%s",\n' "$_end_ts"
|
||||
printf ' "kvm": "%s",\n' "${KVM_STATUS:-unknown}"
|
||||
printf ' "trace_level": "%s",\n' "${HIPERISO_TRACE_LEVEL:-standard}"
|
||||
printf ' "guest_ram_mb": %s,\n' "${HIPERISO_GUEST_RAM:-2048}"
|
||||
printf ' "guest_cpus": %s,\n' "${HIPERISO_GUEST_CPUS:-2}"
|
||||
if [ -n "$_qexit" ]; then
|
||||
printf ' "qemu_exit_code": %s,\n' "$_qexit"
|
||||
else
|
||||
printf ' "qemu_exit_code": null,\n'
|
||||
fi
|
||||
printf ' "report_available": %s,\n' "$_report_avail"
|
||||
|
||||
# Host metadata: non-identifying fields from /proc, already
|
||||
# captured in env.txt — surfaced here for structured ingestion.
|
||||
_host_kernel=$(head -n 1 /proc/version 2>/dev/null)
|
||||
_host_cpu_model=$(grep '^model name' /proc/cpuinfo 2>/dev/null | head -n 1 | cut -d: -f2- | sed 's/^[[:space:]]*//')
|
||||
_host_cpu_logical=$(grep -c '^processor' /proc/cpuinfo 2>/dev/null)
|
||||
_host_ram_kb=$(awk '/^MemTotal:/ { print $2 }' /proc/meminfo 2>/dev/null)
|
||||
_host_ram_mb=$(( _host_ram_kb / 1024 ))
|
||||
if grep -q -E '(vmx|svm)' /proc/cpuinfo 2>/dev/null; then
|
||||
_host_virt="true"
|
||||
else
|
||||
_host_virt="false"
|
||||
fi
|
||||
|
||||
printf ' "host": {\n'
|
||||
printf ' "kernel_version": "%s",\n' "$(hiperiso_json_escape "${_host_kernel:-}")"
|
||||
printf ' "cpu_model": "%s",\n' "$(hiperiso_json_escape "${_host_cpu_model:-}")"
|
||||
printf ' "cpu_logical_cores": %s,\n' "${_host_cpu_logical:-0}"
|
||||
printf ' "total_ram_mb": %s,\n' "${_host_ram_mb:-0}"
|
||||
printf ' "virt_supported": %s\n' "$_host_virt"
|
||||
printf ' },\n'
|
||||
printf ' "analysis": {\n'
|
||||
printf ' "boot_result": "%s",\n' "${HIPERISO_A_BOOT_RESULT:-unknown}"
|
||||
printf ' "boot_stage_final": "%s",\n' "${HIPERISO_A_BOOT_STAGE:-unknown}"
|
||||
printf ' "failure_domain": "%s",\n' "${HIPERISO_A_FAILURE_DOMAIN:-unknown}"
|
||||
printf ' "reached_login": %s,\n' "${HIPERISO_A_REACHED_LOGIN:-0}"
|
||||
printf ' "kernel_panic_count": %s,\n' "${HIPERISO_A_KERNEL_PANIC:-0}"
|
||||
printf ' "error_count": %s,\n' "${HIPERISO_A_ERROR_COUNT:-0}"
|
||||
printf ' "has_serial": %s,\n' "${HIPERISO_A_HAS_SERIAL:-0}"
|
||||
printf ' "has_trace": %s,\n' "${HIPERISO_A_HAS_TRACE:-0}"
|
||||
printf ' "boot_duration_ms": %s,\n' "${HIPERISO_A_DURATION_MS:-0}"
|
||||
printf ' "graphics_drivers": "%s",\n' "$_gdrv_esc"
|
||||
printf ' "graphics_resolution": "%s"\n' "$_gres_esc"
|
||||
printf ' },\n'
|
||||
|
||||
# hw_inventory: list which hardware data files exist so agents
|
||||
# know what's available without scanning the directory.
|
||||
_hw_dir="${LOG_DIR}/hw"
|
||||
_hw_files=""
|
||||
if [ -d "$_hw_dir" ]; then
|
||||
_hw_files=$(ls "$_hw_dir" 2>/dev/null | tr '\n' ',' | sed 's/,$//')
|
||||
fi
|
||||
|
||||
printf ' "hw_inventory": {\n'
|
||||
printf ' "available": %s,\n' $([ -n "$_hw_files" ] && echo true || echo false)
|
||||
printf ' "files": "%s",\n' "$(hiperiso_json_escape "$_hw_files")"
|
||||
printf ' "has_timing": %s,\n' $([ -f "${LOG_DIR}/timing.json" ] && echo true || echo false)
|
||||
printf ' "has_network_pcap": %s\n' $([ -f "${LOG_DIR}/network.pcap" ] && echo true || echo false)
|
||||
printf ' }\n'
|
||||
printf '}\n'
|
||||
} > "$_file"
|
||||
sync "$_file" 2>/dev/null || sync
|
||||
}
|
||||
|
||||
# ── hiperiso_append_global_index() ────────────────────────────
|
||||
# Append one JSONL record to /hiperiso/logs/index.jsonl — the
|
||||
# append-only corpus-level index. Creates the file if absent.
|
||||
# One compact JSON object per line so failed + successful sessions
|
||||
# are both indexed for downstream ingestion.
|
||||
# Args: $1=status $2=qemu_exit_code $3=start_utc $4=end_utc
|
||||
# $5=report_available(0|1)
|
||||
hiperiso_append_global_index() {
|
||||
[ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ] || return 1
|
||||
|
||||
_status="$1"
|
||||
_qexit="${2:-}"
|
||||
_start_ts="${3:-unknown}"
|
||||
_end_ts="${4:-unknown}"
|
||||
_report_avail="${5:-0}"
|
||||
|
||||
# LOG_DIR = .../hiperiso/logs/<session>/ -> parent = .../hiperiso/logs
|
||||
_idx_parent=$(dirname "$LOG_DIR")
|
||||
_idx="${_idx_parent}/index.jsonl"
|
||||
_sid=$(hiperiso_session_id)
|
||||
_iso_basename="${HIPERISO_ISO_PATH##*/}"
|
||||
_iso_esc=$(hiperiso_json_escape "${HIPERISO_ISO_PATH:-}")
|
||||
_log_esc=$(hiperiso_json_escape "${HIPERISO_LOG_DIR%/}")
|
||||
_gdrv_esc=$(hiperiso_json_escape "${HIPERISO_A_GRAPHICS_DRIVERS:-}")
|
||||
|
||||
_result="${HIPERISO_A_BOOT_RESULT:-}"
|
||||
[ -n "$_result" ] || _result="$_status"
|
||||
|
||||
{
|
||||
printf '{"session_id":"%s","status":"%s","result":"%s",' \
|
||||
"$_sid" "$_status" "$_result"
|
||||
printf '"iso_basename":"%s","iso_path":"%s","log_dir":"%s",' \
|
||||
"$(hiperiso_json_escape "$_iso_basename")" "$_iso_esc" "$_log_esc"
|
||||
printf '"start_time_utc":"%s","end_time_utc":"%s",' \
|
||||
"$_start_ts" "$_end_ts"
|
||||
printf '"kvm":"%s","trace_level":"%s",' \
|
||||
"${KVM_STATUS:-unknown}" "${HIPERISO_TRACE_LEVEL:-standard}"
|
||||
if [ -n "$_qexit" ]; then
|
||||
printf '"qemu_exit_code":%s,' "$_qexit"
|
||||
else
|
||||
printf '"qemu_exit_code":null,'
|
||||
fi
|
||||
printf '"report_available":%s,' "$_report_avail"
|
||||
printf '"boot_result":"%s","boot_stage_final":"%s","failure_domain":"%s",' \
|
||||
"${HIPERISO_A_BOOT_RESULT:-unknown}" \
|
||||
"${HIPERISO_A_BOOT_STAGE:-unknown}" \
|
||||
"${HIPERISO_A_FAILURE_DOMAIN:-unknown}"
|
||||
printf '"graphics_drivers":"%s"}\n' "$_gdrv_esc"
|
||||
} >> "$_idx"
|
||||
sync "$_idx" 2>/dev/null || sync
|
||||
}
|
||||
|
||||
# ── hiperiso_finalize_session() ───────────────────────────────
|
||||
# Finalize the boot session: rewrite the canonical session.json
|
||||
# manifest with full end/result metadata, update status.json,
|
||||
# append to the global JSONL index, and write the completion
|
||||
# sentinel. Called on BOTH success and failure paths so every
|
||||
# session — including failed ones — is indexed.
|
||||
# Args: $1=status (complete|failed)
|
||||
# $2=qemu_exit_code (optional)
|
||||
# $3=stage label (optional, e.g. session_complete|iso_not_found|
|
||||
# fallback_no_kvm)
|
||||
hiperiso_finalize_session() {
|
||||
[ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ] || return 1
|
||||
|
||||
_status="${1:-complete}"
|
||||
_qexit="${2:-}"
|
||||
_stage="${3:-session_complete}"
|
||||
|
||||
_end_ts=$(hiperiso_utc_iso)
|
||||
_start_ts="${HIPERISO_SESSION_START:-unknown}"
|
||||
|
||||
hiperiso_read_analysis_meta
|
||||
|
||||
if [ -f "${LOG_DIR}/report.json" ]; then
|
||||
_report_avail=1
|
||||
else
|
||||
_report_avail=0
|
||||
fi
|
||||
|
||||
hiperiso_write_final_manifest "$_status" "$_qexit" \
|
||||
"$_start_ts" "$_end_ts" "$_report_avail"
|
||||
hiperiso_write_status "$_status" "$_stage" "$_qexit"
|
||||
hiperiso_append_global_index "$_status" "$_qexit" \
|
||||
"$_start_ts" "$_end_ts" "$_report_avail"
|
||||
hiperiso_mark_session_complete
|
||||
|
||||
hiperiso_log "session finalized: status=$_status result=${HIPERISO_A_BOOT_RESULT:-n/a}"
|
||||
hiperiso_log "global index: $(dirname "$LOG_DIR")/index.jsonl"
|
||||
}
|
||||
|
||||
# ── hiperiso_timing_mark() ────────────────────────────────────
|
||||
# Record a boot phase timing mark. Uses /proc/uptime (seconds.microseconds
|
||||
# since kernel boot) as a monotonic clock. Appends to ${LOG_DIR}/timing.dat
|
||||
# as raw TSV: phase<TAB>uptime<TAB>iso8601
|
||||
# Args: $1 = phase name (e.g. kernel_start, data_mounted, qemu_started)
|
||||
hiperiso_timing_mark() {
|
||||
[ -n "${LOG_DIR:-}" ] && [ -d "$LOG_DIR" ] || return 0
|
||||
|
||||
_phase="$1"
|
||||
_up=$(cut -d' ' -f1 /proc/uptime 2>/dev/null)
|
||||
_ts=$(hiperiso_utc_iso)
|
||||
|
||||
printf '%s\t%s\t%s\n' "$_phase" "${_up:-0}" "$_ts" >> "${LOG_DIR}/timing.dat"
|
||||
}
|
||||
|
||||
# ── hiperiso_timing_write_json() ──────────────────────────────
|
||||
# Convert timing.dat into structured timing.json for agent consumption.
|
||||
# Calculates per-phase deltas and total duration. Called at session end.
|
||||
hiperiso_timing_write_json() {
|
||||
_dat="${LOG_DIR}/timing.dat"
|
||||
_out="${LOG_DIR}/timing.json"
|
||||
[ -f "$_dat" ] || return 0
|
||||
|
||||
# First and last uptime values for total duration
|
||||
_first_up=$(head -n1 "$_dat" | cut -f2)
|
||||
_last_up=$(tail -n1 "$_dat" | cut -f2)
|
||||
_duration=""
|
||||
if [ -n "$_first_up" ] && [ -n "$_last_up" ]; then
|
||||
_duration=$(awk "BEGIN { printf \"%.3f\", $_last_up - $_first_up }" 2>/dev/null)
|
||||
fi
|
||||
|
||||
{
|
||||
printf '{\n'
|
||||
printf ' "total_duration_s": %s,\n' "${_duration:-null}"
|
||||
printf ' "phases": [\n'
|
||||
|
||||
_prev_up=""
|
||||
_first=1
|
||||
while IFS="$(printf '\t')" read -r _phase _up _ts || [ -n "$_phase" ]; do
|
||||
[ -n "$_phase" ] || continue
|
||||
|
||||
if [ "$_first" -eq 1 ]; then
|
||||
_first=0
|
||||
else
|
||||
printf ',\n'
|
||||
fi
|
||||
|
||||
# Delta from previous phase
|
||||
_delta=""
|
||||
if [ -n "$_prev_up" ] && [ -n "$_up" ]; then
|
||||
_delta=$(awk "BEGIN { printf \"%.3f\", $_up - $_prev_up }" 2>/dev/null)
|
||||
fi
|
||||
|
||||
printf ' {"phase": "%s", "uptime_s": %s, "timestamp_utc": "%s", "delta_s": %s}' \
|
||||
"$_phase" "${_up:-0}" "$_ts" "${_delta:-null}"
|
||||
|
||||
_prev_up="$_up"
|
||||
done < "$_dat"
|
||||
|
||||
printf '\n ]\n'
|
||||
printf '}\n'
|
||||
} > "$_out"
|
||||
sync "$_out" 2>/dev/null || sync
|
||||
}
|
||||
Executable
+506
@@ -0,0 +1,506 @@
|
||||
#!/bin/sh
|
||||
# ============================================================
|
||||
# hw_collect.sh -- Hardware inventory collector for hiperiso
|
||||
#
|
||||
# Collects comprehensive hardware data across three phases:
|
||||
#
|
||||
# hw_collect.sh pre Before QEMU: host /proc, /sys, ACPI,
|
||||
# SMBIOS, KVM params, USB, EFI
|
||||
# hw_collect.sh monitor Background: send HMP commands to QEMU
|
||||
# via FIFO pipe chardev (18 commands)
|
||||
# hw_collect.sh post After QEMU: parse raw dump, generate
|
||||
# JSON summaries, extract crash data
|
||||
#
|
||||
# Output directory: ${LOG_DIR}/hw/
|
||||
#
|
||||
# Host files (always present if pre-phase ran):
|
||||
# host_cpuinfo.txt /proc/cpuinfo
|
||||
# host_meminfo.txt /proc/meminfo
|
||||
# host_interrupts.txt /proc/interrupts
|
||||
# host_iomem.txt /proc/iomem (physical memory map)
|
||||
# host_ioports.txt /proc/ioports
|
||||
# host_cmdline.txt /proc/cmdline
|
||||
# host_pci_devices.txt /proc/bus/pci/devices
|
||||
# host_cpu_cache.txt CPU cache topology from sysfs
|
||||
# host_numa.txt NUMA node topology
|
||||
# host_iommu.txt IOMMU groups (VT-d/AMD-Vi)
|
||||
# host_kvm.txt /dev/kvm presence
|
||||
# host_block.txt Block device info
|
||||
# host_dmesg.txt Host kernel boot messages
|
||||
# host_version.txt /proc/version
|
||||
# host_diskstats.txt /proc/diskstats
|
||||
# host_usb.txt USB device tree from sysfs
|
||||
# host_dmi.txt DMI/SMBIOS identifiers from sysfs
|
||||
# acpi/ Host ACPI tables (binary .aml files)
|
||||
# smbios/ Host DMI/SMBIOS raw tables
|
||||
# kvm_caps.json KVM capabilities (module params)
|
||||
#
|
||||
# QEMU files (present if QEMU launched and ran >= 3 seconds):
|
||||
# qemu_version.txt QEMU version string
|
||||
# qemu_qtree.txt Full QEMU device tree
|
||||
# qemu_pci.txt PCI device list as seen by guest
|
||||
# qemu_memmap.txt Guest memory map (mtree)
|
||||
# qemu_memmap_flat.txt Flat memory map (mtree -f)
|
||||
# qemu_cpuid.txt CPUID leaves exposed to guest
|
||||
# qemu_chardev.txt QEMU character devices
|
||||
# qemu_block.txt QEMU block devices
|
||||
# qemu_net.txt QEMU network devices
|
||||
# qemu_ioapic.txt IO-APIC interrupt routing
|
||||
# qemu_lapic.txt Local APIC state
|
||||
# qemu_registers.txt CPU register snapshot
|
||||
# qemu_tlb.txt TLB state (TCG only)
|
||||
# qemu_numa.txt Guest NUMA topology
|
||||
# qemu_hpet.txt HPET timer state
|
||||
# qemu_irq.txt IRQ statistics
|
||||
# qemu_qomtree.txt QOM object hierarchy
|
||||
# qemu_smbios.txt SMBIOS table (if QEMU supports it)
|
||||
# pci_summary.json Structured PCI device list
|
||||
# ============================================================
|
||||
|
||||
. /hiperiso-lib.sh
|
||||
|
||||
LOG_DIR="${LOG_DIR:?hw_collect: LOG_DIR not set}"
|
||||
HW_DIR="${LOG_DIR}/hw"
|
||||
MON_IN="/tmp/hw_mon.in"
|
||||
MON_OUT="/tmp/hw_mon.out"
|
||||
|
||||
mkdir -p "$HW_DIR"
|
||||
|
||||
# ── Mode: pre ────────────────────────────────────────────────
|
||||
hw_collect_pre() {
|
||||
hiperiso_log "hw_collect: collecting host hardware inventory..."
|
||||
|
||||
# ── /proc snapshots ──────────────────────────────────────
|
||||
cat /proc/cpuinfo > "$HW_DIR/host_cpuinfo.txt" 2>/dev/null
|
||||
cat /proc/meminfo > "$HW_DIR/host_meminfo.txt" 2>/dev/null
|
||||
cat /proc/version > "$HW_DIR/host_version.txt" 2>/dev/null
|
||||
cat /proc/diskstats > "$HW_DIR/host_diskstats.txt" 2>/dev/null
|
||||
|
||||
[ -f /proc/interrupts ] && cat /proc/interrupts > "$HW_DIR/host_interrupts.txt" 2>/dev/null
|
||||
[ -f /proc/iomem ] && cat /proc/iomem > "$HW_DIR/host_iomem.txt" 2>/dev/null
|
||||
[ -f /proc/ioports ] && cat /proc/ioports > "$HW_DIR/host_ioports.txt" 2>/dev/null
|
||||
[ -f /proc/cmdline ] && cat /proc/cmdline > "$HW_DIR/host_cmdline.txt" 2>/dev/null
|
||||
[ -f /proc/bus/pci/devices ] && cat /proc/bus/pci/devices > "$HW_DIR/host_pci_devices.txt" 2>/dev/null
|
||||
|
||||
# ── CPU cache topology ───────────────────────────────────
|
||||
{
|
||||
printf '=== CPU Cache Topology ===\n'
|
||||
for _idx in /sys/devices/system/cpu/cpu*/cache/index*/; do
|
||||
[ -d "$_idx" ] || continue
|
||||
_level=$(cat "${_idx}level" 2>/dev/null)
|
||||
_type=$(cat "${_idx}type" 2>/dev/null)
|
||||
_size=$(cat "${_idx}size" 2>/dev/null)
|
||||
_shared=$(cat "${_idx}shared_cpu_list" 2>/dev/null)
|
||||
printf 'cpu=%s level=%s type=%s size=%s shared=%s\n' \
|
||||
"$(printf '%s' "$_idx" | sed 's|.*/cpu\(cpu[0-9]*\)/.*|\1|')" \
|
||||
"$_level" "$_type" "$_size" "$_shared"
|
||||
done
|
||||
} > "$HW_DIR/host_cpu_cache.txt" 2>/dev/null
|
||||
|
||||
# ── CPU topology (thread/core/socket IDs) ────────────────
|
||||
{
|
||||
printf '=== CPU Topology ===\n'
|
||||
for _cpu in /sys/devices/system/cpu/cpu[0-9]*; do
|
||||
[ -d "$_cpu" ] || continue
|
||||
_name=$(basename "$_cpu")
|
||||
_core=$(cat "${_cpu}/topology/core_id" 2>/dev/null)
|
||||
_sock=$(cat "${_cpu}/topology/physical_package_id" 2>/dev/null)
|
||||
_threads=$(cat "${_cpu}/topology/thread_siblings_list" 2>/dev/null)
|
||||
printf '%s: core=%s socket=%s siblings=%s\n' \
|
||||
"$_name" "${_core:-?}" "${_sock:-?}" "${_threads:-?}"
|
||||
done
|
||||
} > "$HW_DIR/host_cpu_topology.txt" 2>/dev/null
|
||||
|
||||
# ── NUMA topology ────────────────────────────────────────
|
||||
if [ -d /sys/devices/system/node ]; then
|
||||
{
|
||||
printf '=== NUMA Topology ===\n'
|
||||
for _node in /sys/devices/system/node/node[0-9]*; do
|
||||
[ -d "$_node" ] || continue
|
||||
_name=$(basename "$_node")
|
||||
_cpus=$(cat "${_node}/cpulist" 2>/dev/null)
|
||||
printf '%s: cpus=%s\n' "$_name" "$_cpus"
|
||||
done
|
||||
} > "$HW_DIR/host_numa.txt" 2>/dev/null
|
||||
fi
|
||||
|
||||
# ── IOMMU groups (VT-d / AMD-Vi) ─────────────────────────
|
||||
if [ -d /sys/kernel/iommu_groups ]; then
|
||||
{
|
||||
printf '=== IOMMU Groups ===\n'
|
||||
for _grp in /sys/kernel/iommu_groups/[0-9]*; do
|
||||
[ -d "$_grp" ] || continue
|
||||
_gid=$(basename "$_grp")
|
||||
printf 'group %s:\n' "$_gid"
|
||||
for _dev in "$_grp"/devices/*; do
|
||||
[ -e "$_dev" ] || continue
|
||||
printf ' %s\n' "$(basename "$_dev")"
|
||||
done
|
||||
done
|
||||
} > "$HW_DIR/host_iommu.txt" 2>/dev/null
|
||||
fi
|
||||
|
||||
# ── KVM presence ─────────────────────────────────────────
|
||||
if [ -e /dev/kvm ]; then
|
||||
{
|
||||
printf '=== KVM Device ===\n'
|
||||
ls -la /dev/kvm 2>/dev/null
|
||||
} > "$HW_DIR/host_kvm.txt" 2>/dev/null
|
||||
fi
|
||||
|
||||
# ── KVM capabilities (module parameters) ─────────────────
|
||||
_kvm_json="$HW_DIR/kvm_caps.json"
|
||||
{
|
||||
printf '{\n'
|
||||
printf ' "kvm_present": %s,\n' $([ -e /dev/kvm ] && echo true || echo false)
|
||||
|
||||
# Core KVM parameters
|
||||
_first=1
|
||||
printf ' "kvm_core": {'
|
||||
if [ -d /sys/module/kvm/parameters ]; then
|
||||
for _p in /sys/module/kvm/parameters/*; do
|
||||
[ -f "$_p" ] || continue
|
||||
_pname=$(basename "$_p")
|
||||
_pval=$(cat "$_p" 2>/dev/null)
|
||||
if [ "$_first" -eq 1 ]; then _first=0; else printf ','; fi
|
||||
printf '\n "%s": "%s"' "$_pname" "$_pval"
|
||||
done
|
||||
fi
|
||||
[ "$_first" -eq 0 ] && printf '\n '
|
||||
printf '},\n'
|
||||
|
||||
# Intel VT-x parameters
|
||||
_first=1
|
||||
printf ' "kvm_intel": {'
|
||||
if [ -d /sys/module/kvm_intel/parameters ]; then
|
||||
for _p in /sys/module/kvm_intel/parameters/*; do
|
||||
[ -f "$_p" ] || continue
|
||||
_pname=$(basename "$_p")
|
||||
_pval=$(cat "$_p" 2>/dev/null)
|
||||
if [ "$_first" -eq 1 ]; then _first=0; else printf ','; fi
|
||||
printf '\n "%s": "%s"' "$_pname" "$_pval"
|
||||
done
|
||||
fi
|
||||
[ "$_first" -eq 0 ] && printf '\n '
|
||||
printf '},\n'
|
||||
|
||||
# AMD-V parameters
|
||||
_first=1
|
||||
printf ' "kvm_amd": {'
|
||||
if [ -d /sys/module/kvm_amd/parameters ]; then
|
||||
for _p in /sys/module/kvm_amd/parameters/*; do
|
||||
[ -f "$_p" ] || continue
|
||||
_pname=$(basename "$_p")
|
||||
_pval=$(cat "$_p" 2>/dev/null)
|
||||
if [ "$_first" -eq 1 ]; then _first=0; else printf ','; fi
|
||||
printf '\n "%s": "%s"' "$_pname" "$_pval"
|
||||
done
|
||||
fi
|
||||
[ "$_first" -eq 0 ] && printf '\n '
|
||||
printf '}\n'
|
||||
printf '}\n'
|
||||
} > "$_kvm_json" 2>/dev/null
|
||||
|
||||
# ── Block devices ────────────────────────────────────────
|
||||
{
|
||||
printf '=== Block Devices ===\n'
|
||||
for _blk in /sys/block/sd* /sys/block/nvme* /sys/block/sr* /sys/block/mmcblk*; do
|
||||
[ -d "$_blk" ] || continue
|
||||
_name=$(basename "$_blk")
|
||||
_size=$(cat "${_blk}/size" 2>/dev/null)
|
||||
_rm=$(cat "${_blk}/removable" 2>/dev/null)
|
||||
_ro=$(cat "${_blk}/readonly" 2>/dev/null)
|
||||
_model=$(cat "${_blk}/device/model" 2>/dev/null)
|
||||
_vendor=$(cat "${_blk}/device/vendor" 2>/dev/null)
|
||||
printf '%s: sectors=%s removable=%s readonly=%s model=%s vendor=%s\n' \
|
||||
"$_name" "${_size:-?}" "${_rm:-?}" "${_ro:-?}" \
|
||||
"$(printf '%s' "$_model" | tr -d ' ')" \
|
||||
"$(printf '%s' "$_vendor" | tr -d ' ')"
|
||||
done
|
||||
} > "$HW_DIR/host_block.txt" 2>/dev/null
|
||||
|
||||
# ── USB device tree ──────────────────────────────────────
|
||||
{
|
||||
printf '=== USB Devices ===\n'
|
||||
for _dev in /sys/bus/usb/devices/*; do
|
||||
[ -d "$_dev" ] || continue
|
||||
_name=$(basename "$_dev")
|
||||
_vid=$(cat "${_dev}/idVendor" 2>/dev/null)
|
||||
_pid=$(cat "${_dev}/idProduct" 2>/dev/null)
|
||||
_mfr=$(cat "${_dev}/manufacturer" 2>/dev/null)
|
||||
_prod=$(cat "${_dev}/product" 2>/dev/null)
|
||||
_speed=$(cat "${_dev}/speed" 2>/dev/null)
|
||||
[ -n "$_vid" ] || continue
|
||||
printf '%s: %s:%s speed=%s mfr=%s product=%s\n' \
|
||||
"$_name" "$_vid" "$_pid" "${_speed:-?}" \
|
||||
"$(printf '%s' "$_mfr" | tr -d '\n')" \
|
||||
"$(printf '%s' "$_prod" | tr -d '\n')"
|
||||
done
|
||||
} > "$HW_DIR/host_usb.txt" 2>/dev/null
|
||||
|
||||
# ── ACPI tables (host firmware) ──────────────────────────
|
||||
if [ -d /sys/firmware/acpi/tables ]; then
|
||||
mkdir -p "$HW_DIR/acpi"
|
||||
cp -r /sys/firmware/acpi/tables/* "$HW_DIR/acpi/" 2>/dev/null
|
||||
{
|
||||
printf '=== ACPI Tables ===\n'
|
||||
for _t in /sys/firmware/acpi/tables/[A-Z]*; do
|
||||
[ -f "$_t" ] || continue
|
||||
_sig=$(basename "$_t")
|
||||
_size=$(wc -c < "$_t" 2>/dev/null)
|
||||
printf '%s: %s bytes\n' "$_sig" "${_size:-?}"
|
||||
done
|
||||
} > "$HW_DIR/acpi/_summary.txt" 2>/dev/null
|
||||
fi
|
||||
|
||||
# ── DMI / SMBIOS (host firmware) ─────────────────────────
|
||||
if [ -d /sys/firmware/dmi/tables ]; then
|
||||
mkdir -p "$HW_DIR/smbios"
|
||||
cp /sys/firmware/dmi/tables/* "$HW_DIR/smbios/" 2>/dev/null
|
||||
fi
|
||||
if [ -d /sys/class/dmi/id ]; then
|
||||
{
|
||||
printf '=== DMI Identifiers ===\n'
|
||||
for _f in /sys/class/dmi/id/*; do
|
||||
[ -f "$_f" ] || continue
|
||||
_key=$(basename "$_f")
|
||||
_val=$(cat "$_f" 2>/dev/null)
|
||||
[ -n "$_val" ] && printf '%s: %s\n' "$_key" "$_val"
|
||||
done
|
||||
} > "$HW_DIR/host_dmi.txt" 2>/dev/null
|
||||
fi
|
||||
|
||||
# ── EFI firmware info (if UEFI boot) ─────────────────────
|
||||
if [ -d /sys/firmware/efi ]; then
|
||||
{
|
||||
printf '=== EFI Firmware ===\n'
|
||||
cat /sys/firmware/efi/fw_platform_size 2>/dev/null
|
||||
printf 'Runtime services: '
|
||||
ls /sys/firmware/efi/runtime 2>/dev/null || printf '(not exported)\n'
|
||||
printf 'EFI variables:\n'
|
||||
ls /sys/firmware/efi/efivars/ 2>/dev/null | head -20
|
||||
} > "$HW_DIR/host_efi.txt" 2>/dev/null
|
||||
fi
|
||||
|
||||
# ── Kernel config (if available) ─────────────────────────
|
||||
if [ -f /proc/config.gz ]; then
|
||||
cp /proc/config.gz "$HW_DIR/host_kernel_config.gz" 2>/dev/null
|
||||
fi
|
||||
|
||||
# ── DMESG (host kernel messages) ─────────────────────────
|
||||
if command -v dmesg >/dev/null 2>&1; then
|
||||
dmesg > "$HW_DIR/host_dmesg.txt" 2>/dev/null
|
||||
fi
|
||||
|
||||
_count=$(ls "$HW_DIR"/host_*.txt "$HW_DIR"/*.json 2>/dev/null | wc -l)
|
||||
hiperiso_log "hw_collect: host inventory complete ($_count files)"
|
||||
}
|
||||
|
||||
# ── Mode: monitor ────────────────────────────────────────────
|
||||
hw_setup_monitor_fifo() {
|
||||
mkfifo "$MON_IN" "$MON_OUT" 2>/dev/null
|
||||
|
||||
cat "$MON_OUT" > "${HW_DIR}/qemu_monitor_raw.txt" 2>/dev/null &
|
||||
MON_READER_PID=$!
|
||||
printf '%s\n' "$MON_READER_PID" > /tmp/hw_mon_reader.pid
|
||||
|
||||
# Writer: sends HMP commands via echo-delimited markers.
|
||||
# QEMU pipe mode does not echo input, so we inject markers
|
||||
# using QEMU's HMP 'echo' command to enable section splitting.
|
||||
(
|
||||
sleep 3
|
||||
|
||||
# ── P0: Core inventory ───────────────────────────────
|
||||
printf 'echo ===SECTION===version\n'; sleep 0.1
|
||||
printf 'info version\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===qtree\n'; sleep 0.1
|
||||
printf 'info qtree\n'; sleep 0.5
|
||||
printf 'echo ===SECTION===pci\n'; sleep 0.1
|
||||
printf 'info pci\n'; sleep 0.3
|
||||
|
||||
# ── P1: CPU + memory map ─────────────────────────────
|
||||
printf 'echo ===SECTION===cpuid\n'; sleep 0.1
|
||||
printf 'info cpuid\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===memmap\n'; sleep 0.1
|
||||
printf 'info mtree\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===memmap_flat\n'; sleep 0.1
|
||||
printf 'info mtree -f\n'; sleep 0.3
|
||||
|
||||
# ── P2: SMBIOS + registers + TLB ─────────────────────
|
||||
printf 'echo ===SECTION===smbios\n'; sleep 0.1
|
||||
printf 'info smbios\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===registers\n'; sleep 0.1
|
||||
printf 'info registers\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===tlb\n'; sleep 0.1
|
||||
printf 'info tlb\n'; sleep 0.3
|
||||
|
||||
# ── P3: Interrupt routing + KVM caps ─────────────────
|
||||
printf 'echo ===SECTION===ioapic\n'; sleep 0.1
|
||||
printf 'info ioapic\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===lapic\n'; sleep 0.1
|
||||
printf 'info lapic\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===irq\n'; sleep 0.1
|
||||
printf 'info irq\n'; sleep 0.3
|
||||
|
||||
# ── Additional: topology + timers ────────────────────
|
||||
printf 'echo ===SECTION===numa\n'; sleep 0.1
|
||||
printf 'info numa\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===hpet\n'; sleep 0.1
|
||||
printf 'info hpet\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===qomtree\n'; sleep 0.1
|
||||
printf 'info qom-tree\n'; sleep 0.3
|
||||
|
||||
# ── Device summaries ─────────────────────────────────
|
||||
printf 'echo ===SECTION===chardev\n'; sleep 0.1
|
||||
printf 'info chardev\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===block\n'; sleep 0.1
|
||||
printf 'info block\n'; sleep 0.3
|
||||
printf 'echo ===SECTION===net\n'; sleep 0.1
|
||||
printf 'info networking\n'; sleep 0.3
|
||||
) > "$MON_IN" 2>/dev/null &
|
||||
MON_WRITER_PID=$!
|
||||
printf '%s\n' "$MON_WRITER_PID" > /tmp/hw_mon_writer.pid
|
||||
|
||||
hiperiso_log "hw_collect: monitor FIFO reader (PID $MON_READER_PID)"
|
||||
hiperiso_log "hw_collect: monitor FIFO writer (PID $MON_WRITER_PID)"
|
||||
}
|
||||
|
||||
# ── Mode: post ───────────────────────────────────────────────
|
||||
hw_collect_post() {
|
||||
_raw="${HW_DIR}/qemu_monitor_raw.txt"
|
||||
|
||||
if [ ! -f "$_raw" ] || [ ! -s "$_raw" ]; then
|
||||
hiperiso_log "hw_collect: no QEMU monitor data (raw file missing or empty)"
|
||||
hw_cleanup_procs
|
||||
return 1
|
||||
fi
|
||||
|
||||
hiperiso_log "hw_collect: parsing QEMU monitor output..."
|
||||
|
||||
# Split raw dump into per-section files using echo markers
|
||||
# injected by the writer. Each ===SECTION===xxx line triggers
|
||||
# a section change; subsequent output goes to that file.
|
||||
_awk_script='
|
||||
BEGIN { section = "header"; }
|
||||
/===SECTION===version/ { section = "version"; next; }
|
||||
/===SECTION===qtree/ { section = "qtree"; next; }
|
||||
/===SECTION===pci/ { section = "pci"; next; }
|
||||
/===SECTION===cpuid/ { section = "cpuid"; next; }
|
||||
/===SECTION===memmap_flat/ { section = "memmap_flat"; next; }
|
||||
/===SECTION===memmap/ { section = "memmap"; next; }
|
||||
/===SECTION===smbios/ { section = "smbios"; next; }
|
||||
/===SECTION===registers/ { section = "registers"; next; }
|
||||
/===SECTION===tlb/ { section = "tlb"; next; }
|
||||
/===SECTION===ioapic/ { section = "ioapic"; next; }
|
||||
/===SECTION===lapic/ { section = "lapic"; next; }
|
||||
/===SECTION===irq/ { section = "irq"; next; }
|
||||
/===SECTION===numa/ { section = "numa"; next; }
|
||||
/===SECTION===hpet/ { section = "hpet"; next; }
|
||||
/===SECTION===qomtree/ { section = "qomtree"; next; }
|
||||
/===SECTION===chardev/ { section = "chardev"; next; }
|
||||
/===SECTION===block/ { section = "block"; next; }
|
||||
/===SECTION===net/ { section = "net"; next; }
|
||||
{
|
||||
if (section != "header")
|
||||
print > "'"$HW_DIR"'/qemu_" section ".txt";
|
||||
}
|
||||
'
|
||||
|
||||
awk "$_awk_script" "$_raw" 2>/dev/null
|
||||
|
||||
for _f in \
|
||||
qemu_version qemu_qtree qemu_pci qemu_cpuid \
|
||||
qemu_memmap qemu_memmap_flat qemu_smbios \
|
||||
qemu_registers qemu_tlb qemu_ioapic qemu_lapic \
|
||||
qemu_irq qemu_numa qemu_hpet qemu_qomtree \
|
||||
qemu_chardev qemu_block qemu_net; do
|
||||
_fp="${HW_DIR}/${_f}.txt"
|
||||
if [ -f "$_fp" ] && [ ! -s "$_fp" ]; then
|
||||
rm -f "$_fp"
|
||||
fi
|
||||
done
|
||||
|
||||
# Generate PCI summary JSON from qemu_pci.txt
|
||||
hw_gen_pci_json
|
||||
|
||||
_count=$(ls "$HW_DIR"/qemu_*.txt "$HW_DIR"/*.json 2>/dev/null | wc -l)
|
||||
hiperiso_log "hw_collect: parsed $_count QEMU + JSON files"
|
||||
|
||||
hw_cleanup_procs
|
||||
|
||||
hiperiso_log "hw_collect: inventory files:"
|
||||
ls -la "$HW_DIR"/ 2>/dev/null >> "${LOG_DIR}/env.txt"
|
||||
}
|
||||
|
||||
# ── PCI summary JSON ─────────────────────────────────────────
|
||||
# Parse qemu_pci.txt to extract structured PCI device data.
|
||||
hw_gen_pci_json() {
|
||||
_pci="${HW_DIR}/qemu_pci.txt"
|
||||
_out="${HW_DIR}/pci_summary.json"
|
||||
[ -f "$_pci" ] || return 0
|
||||
|
||||
{
|
||||
printf '{\n'
|
||||
printf ' "devices": [\n'
|
||||
|
||||
awk '
|
||||
/^Bus / {
|
||||
if (found) printf " },\n"
|
||||
found = 1
|
||||
bus = $2; gsub(/,/, "", bus)
|
||||
dev = $4; gsub(/,/, "", dev)
|
||||
fn = $6; gsub(/:/, "", fn)
|
||||
printf " {\"bus\": %s, \"device\": %s, \"function\": %s", bus, dev, fn
|
||||
}
|
||||
/vendor_id = / {
|
||||
gsub(/.*= /, "", $0)
|
||||
printf ", \"vendor_id\": \"%s\"", $NF
|
||||
}
|
||||
/device_id = / {
|
||||
gsub(/.*= /, "", $0)
|
||||
printf ", \"device_id\": \"%s\"", $NF
|
||||
}
|
||||
/class = / {
|
||||
gsub(/.*= /, "", $0)
|
||||
printf ", \"class\": \"%s\"", $NF
|
||||
}
|
||||
/IRQ / {
|
||||
_irq = $2; gsub(/\./, "", _irq)
|
||||
printf ", \"irq\": \"%s\"", _irq
|
||||
}
|
||||
END { if (found) printf " }"; printf "\n" }
|
||||
' "$_pci" 2>/dev/null
|
||||
|
||||
printf ' ]\n'
|
||||
printf '}\n'
|
||||
} > "$_out" 2>/dev/null
|
||||
|
||||
if [ ! -s "$_out" ]; then
|
||||
rm -f "$_out"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Process cleanup ──────────────────────────────────────────
|
||||
hw_cleanup_procs() {
|
||||
rm -f "$MON_IN" "$MON_OUT" 2>/dev/null
|
||||
for _pidfile in /tmp/hw_mon_reader.pid /tmp/hw_mon_writer.pid; do
|
||||
if [ -f "$_pidfile" ]; then
|
||||
_pid=$(cat "$_pidfile" 2>/dev/null)
|
||||
if [ -n "$_pid" ] && kill -0 "$_pid" 2>/dev/null; then
|
||||
kill "$_pid" 2>/dev/null
|
||||
fi
|
||||
rm -f "$_pidfile"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ── Main ─────────────────────────────────────────────────────
|
||||
case "${1:-}" in
|
||||
pre) hw_collect_pre ;;
|
||||
monitor) hw_setup_monitor_fifo ;;
|
||||
post) hw_collect_post ;;
|
||||
*)
|
||||
printf 'Usage: hw_collect.sh {pre|monitor|post}\n' >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Executable
+270
@@ -0,0 +1,270 @@
|
||||
#!/bin/sh
|
||||
# ============================================================
|
||||
# init -- PID 1 for the hiperiso hypervisor host initramfs
|
||||
#
|
||||
# Boots a selected ISO as a KVM guest with full bootlogging.
|
||||
# Kernel cmdline parameters (see INTERFACES.sh):
|
||||
# hiperiso_iso="/ISOs/ubuntu.iso"
|
||||
# hiperiso_log="/hiperiso/logs/ubuntu/"
|
||||
# hiperiso_trace_level="standard|detailed|full|none"
|
||||
# hiperiso_ram=2048 hiperiso_cpus=2
|
||||
# hiperiso_display="none|gtk|vnc"
|
||||
# hiperiso_vga="std|none|virtio"
|
||||
# hiperiso_fallback="1"
|
||||
# ============================================================
|
||||
|
||||
PATH="/usr/bin:/bin:/usr/sbin:/sbin"
|
||||
export PATH
|
||||
|
||||
# ── Step 1: Mount essential virtual filesystems ──────────────
|
||||
mkdir -p /proc /sys /dev /tmp /run
|
||||
|
||||
mount -t proc proc /proc 2>/dev/null
|
||||
mount -t sysfs sysfs /sys 2>/dev/null
|
||||
mount -t devtmpfs devtmpfs /dev 2>/dev/null || mount -t tmpfs tmpfs /dev 2>/dev/null
|
||||
mount -t tmpfs tmpfs /tmp 2>/dev/null
|
||||
mount -t tmpfs tmpfs /run 2>/dev/null
|
||||
|
||||
. /hiperiso-lib.sh
|
||||
|
||||
_INIT_UPTIME=$(cut -d' ' -f1 /proc/uptime 2>/dev/null)
|
||||
|
||||
printf '\n'
|
||||
printf '======================================================\n'
|
||||
printf ' hiperiso hypervisor host -- KVM ISO boot logger \n'
|
||||
printf '======================================================\n'
|
||||
printf '\n'
|
||||
|
||||
# ── Step 2: Parse kernel command line ────────────────────────
|
||||
hiperiso_parse_cmdline
|
||||
|
||||
if [ -z "$HIPERISO_ISO_PATH" ]; then
|
||||
printf '[hiperiso] FATAL: hiperiso_iso= not set on kernel command line\n'
|
||||
printf '[hiperiso] Dropping to recovery shell\n'
|
||||
exec /bin/sh
|
||||
fi
|
||||
|
||||
hiperiso_log "ISO path: $HIPERISO_ISO_PATH"
|
||||
hiperiso_log "Log dir: $HIPERISO_LOG_DIR"
|
||||
hiperiso_log "Trace: ${HIPERISO_TRACE_LEVEL:-standard}"
|
||||
hiperiso_log "RAM: ${HIPERISO_GUEST_RAM:-2048} MB"
|
||||
hiperiso_log "CPUs: ${HIPERISO_GUEST_CPUS:-2}"
|
||||
|
||||
# ── Step 3: Find and mount the USB data partition ────────────
|
||||
DATA_PART=$(hiperiso_find_usb_partition)
|
||||
if [ -z "$DATA_PART" ]; then
|
||||
hiperiso_log "FATAL: Could not find hiperiso USB data partition"
|
||||
hiperiso_log "Scanned: blkid label, /dev/sd[abcd]2, sysfs"
|
||||
exec /bin/sh
|
||||
fi
|
||||
hiperiso_log "Data partition: $DATA_PART"
|
||||
|
||||
mkdir -p "$DATA_MOUNT"
|
||||
_mounted=0
|
||||
for _fstype in exfat ntfs3 ntfs vfat ext4 ext3 ext2; do
|
||||
if mount -t "$_fstype" "$DATA_PART" "$DATA_MOUNT" 2>/dev/null; then
|
||||
hiperiso_log "Mounted $DATA_PART at $DATA_MOUNT (type: $_fstype)"
|
||||
_mounted=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$_mounted" -eq 0 ]; then
|
||||
if mount "$DATA_PART" "$DATA_MOUNT" 2>/dev/null; then
|
||||
hiperiso_log "Mounted $DATA_PART at $DATA_MOUNT (auto-detected)"
|
||||
_mounted=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$_mounted" -eq 0 ]; then
|
||||
hiperiso_log "FATAL: Failed to mount $DATA_PART"
|
||||
exec /bin/sh
|
||||
fi
|
||||
|
||||
# ── Step 4: Mount the EFI System Partition (for OVMF) ────────
|
||||
ESP_PART=$(printf '%s' "$DATA_PART" | sed 's/1$/2/')
|
||||
|
||||
if [ ! -e "$ESP_PART" ]; then
|
||||
for _d in /dev/sda2 /dev/sdb2 /dev/sdc2 /dev/sdd2 /dev/sde2 /dev/sdf2 /dev/nvme0n1p2 /dev/nvme1n1p2 /dev/mmcblk0p2; do
|
||||
if [ -e "$_d" ]; then
|
||||
ESP_PART="$_d"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
mkdir -p "$EFI_MOUNT"
|
||||
if mount -t vfat "$ESP_PART" "$EFI_MOUNT" 2>/dev/null; then
|
||||
hiperiso_log "Mounted ESP $ESP_PART at $EFI_MOUNT"
|
||||
else
|
||||
hiperiso_log "WARNING: Could not mount ESP $ESP_PART -- OVMF may be unavailable"
|
||||
fi
|
||||
|
||||
# ── Step 5: Resolve absolute paths and create log dir ────────
|
||||
# Derive a unique per-session directory with UTC timestamp so
|
||||
# every boot gets its own log set (no overwriting).
|
||||
HIPERISO_LOG_DIR=$(hiperiso_derive_session_dir "$HIPERISO_LOG_DIR")
|
||||
export HIPERISO_LOG_DIR
|
||||
|
||||
LOG_DIR="${DATA_MOUNT}${HIPERISO_LOG_DIR}"
|
||||
ISO_PATH="${DATA_MOUNT}${HIPERISO_ISO_PATH}"
|
||||
OVMF_FULL_PATH="${EFI_MOUNT}${OVMF_PATH}"
|
||||
|
||||
# Collision-safe: if session dir already exists (e.g. same-second boot),
|
||||
# append an incrementing suffix so logs are never overwritten.
|
||||
if [ -d "$LOG_DIR" ]; then
|
||||
_i=2
|
||||
while [ -d "${LOG_DIR%/}_${_i}" ]; do
|
||||
_i=$((_i + 1))
|
||||
done
|
||||
LOG_DIR="${LOG_DIR%/}_${_i}/"
|
||||
HIPERISO_LOG_DIR="${HIPERISO_LOG_DIR%/}_${_i}/"
|
||||
fi
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
export LOG_DIR ISO_PATH OVMF_FULL_PATH EFI_MOUNT DATA_MOUNT
|
||||
|
||||
# ── Record early timing marks retroactively ──────────────────
|
||||
# These phases elapsed before LOG_DIR existed; capture them now
|
||||
# using the uptime values saved at each checkpoint.
|
||||
printf 'kernel_start\t%s\t%s\n' "${_INIT_UPTIME:-0}" "$(hiperiso_utc_iso)" >> "${LOG_DIR}/timing.dat"
|
||||
hiperiso_timing_mark "session_created"
|
||||
|
||||
hiperiso_log "Session dir: $HIPERISO_LOG_DIR"
|
||||
|
||||
# ── Step 6: Write session metadata and environment snapshot ──
|
||||
hiperiso_write_session_meta "started"
|
||||
hiperiso_write_status "running" "mounted"
|
||||
hiperiso_write_env
|
||||
hiperiso_timing_mark "env_written"
|
||||
|
||||
# ── Step 7: KVM detection ────────────────────────────────────
|
||||
KVM_RESULT=$(/kvm_check.sh)
|
||||
printf 'KVM_CHECK_RESULT=%s\n' "$KVM_RESULT" >> "${LOG_DIR}/env.txt"
|
||||
|
||||
case "$KVM_RESULT" in
|
||||
KVM_OK)
|
||||
hiperiso_log "KVM available -- proceeding with hypervisor boot"
|
||||
export KVM_AVAILABLE=1
|
||||
export KVM_STATUS="available"
|
||||
hiperiso_write_status "running" "kvm_ok"
|
||||
;;
|
||||
KVM_FAIL:*)
|
||||
hiperiso_log "KVM check failed: ${KVM_RESULT#KVM_FAIL:}"
|
||||
export KVM_AVAILABLE=0
|
||||
export KVM_STATUS="unavailable"
|
||||
if [ "$HIPERISO_FALLBACK" != "1" ]; then
|
||||
hiperiso_write_status "failed" "fallback_no_kvm"
|
||||
/fallback_boot.sh
|
||||
exec /bin/sh
|
||||
fi
|
||||
hiperiso_log "Fallback forced -- continuing with TCG emulation"
|
||||
hiperiso_write_status "running" "tcg_emulation"
|
||||
;;
|
||||
esac
|
||||
hiperiso_timing_mark "kvm_checked"
|
||||
|
||||
# ── Step 8: Verify ISO exists ────────────────────────────────
|
||||
if [ ! -f "$ISO_PATH" ]; then
|
||||
hiperiso_log "FATAL: ISO file not found: $ISO_PATH"
|
||||
hiperiso_log "Contents of $DATA_MOUNT:"
|
||||
ls -la "$DATA_MOUNT" 2>/dev/null >> "${LOG_DIR}/env.txt"
|
||||
hiperiso_write_status "failed" "iso_not_found"
|
||||
hiperiso_run_report
|
||||
hiperiso_finalize_session "failed" "" "iso_not_found"
|
||||
exec /bin/sh
|
||||
fi
|
||||
hiperiso_log "ISO found: $ISO_PATH"
|
||||
hiperiso_timing_mark "iso_verified"
|
||||
|
||||
# ── Step 8b: Run conf_replace plugin (if configured) ─────────
|
||||
_modified_iso=$(sh /conf_replace.sh 2>"${LOG_DIR}/conf_replace.log" || true)
|
||||
if [ -n "$_modified_iso" ] && [ -f "$_modified_iso" ]; then
|
||||
ISO_PATH="$_modified_iso"
|
||||
export ISO_PATH
|
||||
hiperiso_log "Using modified ISO: $ISO_PATH"
|
||||
fi
|
||||
|
||||
# ── Step 9: Start log flush daemon ───────────────────────────
|
||||
rm -f /tmp/hiperiso_done
|
||||
/log_flush.sh "$LOG_DIR" &
|
||||
LOG_FLUSH_PID=$!
|
||||
hiperiso_log "Log flush daemon started (PID $LOG_FLUSH_PID)"
|
||||
|
||||
hiperiso_write_status "running" "launching_qemu"
|
||||
hiperiso_timing_mark "qemu_launching"
|
||||
|
||||
# ── Step 10: Launch QEMU ─────────────────────────────────────
|
||||
/qemu_launch.sh
|
||||
QEMU_EXIT_CODE=$?
|
||||
|
||||
# ── Step 11: Flush remaining logs ────────────────────────────
|
||||
touch /tmp/hiperiso_done
|
||||
hiperiso_timing_mark "qemu_returned"
|
||||
|
||||
_wait=0
|
||||
while kill -0 "$LOG_FLUSH_PID" 2>/dev/null; do
|
||||
_wait=$((_wait + 1))
|
||||
if [ "$_wait" -gt 10 ]; then
|
||||
kill "$LOG_FLUSH_PID" 2>/dev/null
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
sync
|
||||
printf 'QEMU_EXIT_CODE=%s\n' "$QEMU_EXIT_CODE" >> "${LOG_DIR}/env.txt"
|
||||
|
||||
hiperiso_write_status "running" "qemu_exited" "$QEMU_EXIT_CODE"
|
||||
|
||||
if [ "$QEMU_EXIT_CODE" -eq 0 ]; then
|
||||
_session_status="complete"
|
||||
_session_stage="session_complete"
|
||||
else
|
||||
_session_status="failed"
|
||||
_session_stage="qemu_error"
|
||||
fi
|
||||
|
||||
hiperiso_run_report
|
||||
hiperiso_timing_mark "report_done"
|
||||
hiperiso_finalize_session "$_session_status" "$QEMU_EXIT_CODE" "$_session_stage"
|
||||
hiperiso_timing_mark "session_finalized"
|
||||
hiperiso_timing_write_json
|
||||
|
||||
# ── Step 12: Print session summary ───────────────────────────
|
||||
printf '\n'
|
||||
printf '======================================================\n'
|
||||
printf ' hiperiso boot session complete \n'
|
||||
printf '======================================================\n'
|
||||
printf ' ISO: %s\n' "$HIPERISO_ISO_PATH"
|
||||
printf ' Session: %s\n' "$(hiperiso_session_id)"
|
||||
printf ' QEMU exit: %s\n' "$QEMU_EXIT_CODE"
|
||||
printf ' Logs: %s\n' "$LOG_DIR"
|
||||
printf '======================================================\n'
|
||||
printf '\n'
|
||||
printf 'Log files:\n'
|
||||
ls -la "$LOG_DIR" 2>/dev/null
|
||||
printf '\n'
|
||||
|
||||
# ── Step 13: Offer reboot ────────────────────────────────────
|
||||
printf 'Press ENTER to reboot, or type "shell" for recovery: '
|
||||
read -r _response
|
||||
|
||||
case "$_response" in
|
||||
shell|sh)
|
||||
printf '\nDropping to recovery shell...\n'
|
||||
exec /bin/sh
|
||||
;;
|
||||
esac
|
||||
|
||||
printf '\nRebooting...\n'
|
||||
sync
|
||||
|
||||
if command -v reboot >/dev/null 2>&1; then
|
||||
reboot -f
|
||||
else
|
||||
printf 'b' > /proc/sysrq-trigger 2>/dev/null
|
||||
fi
|
||||
|
||||
# Kernel will panic if PID 1 exits -- but reboot should have fired
|
||||
exec /bin/sh
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
#!/bin/sh
|
||||
# ============================================================
|
||||
# kvm_check.sh -- Detect KVM / hardware virtualization support
|
||||
# Output: "KVM_OK" on success
|
||||
# "KVM_FAIL:<reason>" on failure
|
||||
# Exit: 0 on success, 1 on failure
|
||||
# ============================================================
|
||||
|
||||
# ── Helper: attempt to load a kernel module ──────────────────
|
||||
_try_modprobe() {
|
||||
if command -v modprobe >/dev/null 2>&1; then
|
||||
modprobe "$1" 2>/dev/null
|
||||
return $?
|
||||
fi
|
||||
if command -v insmod >/dev/null 2>&1; then
|
||||
# Fallback: try insmod with common module paths
|
||||
for _modpath in \
|
||||
"/lib/modules/$(uname -r 2>/dev/null)/kernel/arch/x86/kvm/$1.ko" \
|
||||
"/lib/modules/$(uname -r 2>/dev/null)/$1.ko" \
|
||||
"/$1.ko"; do
|
||||
if [ -f "$_modpath" ]; then
|
||||
insmod "$_modpath" 2>/dev/null && return 0
|
||||
fi
|
||||
done
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Check 1: /dev/kvm already exists ─────────────────────────
|
||||
if [ -c /dev/kvm ]; then
|
||||
printf 'KVM_OK\n'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Check 2: CPU supports virtualization ─────────────────────
|
||||
# vmx = Intel VT-x, svm = AMD-V
|
||||
if ! grep -q -E '(vmx|svm)' /proc/cpuinfo 2>/dev/null; then
|
||||
printf 'KVM_FAIL:no_hw_virt (CPU lacks vmx/svm flags -- check BIOS/UEFI settings)\n'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Determine CPU vendor for correct KVM module ──────────────
|
||||
_cpu_vendor=""
|
||||
if [ -f /proc/cpuinfo ]; then
|
||||
_cpu_vendor=$(grep -m1 '^vendor_id' /proc/cpuinfo 2>/dev/null \
|
||||
| cut -d: -f2 | tr -d ' ')
|
||||
fi
|
||||
|
||||
# ── Check 3: Load KVM kernel modules ─────────────────────────
|
||||
_try_modprobe kvm
|
||||
|
||||
case "$_cpu_vendor" in
|
||||
GenuineIntel)
|
||||
_try_modprobe kvm-intel
|
||||
;;
|
||||
AuthenticAMD)
|
||||
_try_modprobe kvm-amd
|
||||
;;
|
||||
*)
|
||||
# Unknown vendor -- try both
|
||||
_try_modprobe kvm-intel 2>/dev/null || _try_modprobe kvm-amd 2>/dev/null
|
||||
;;
|
||||
esac
|
||||
|
||||
# Give devtmpfs/udev a moment to create the device node
|
||||
sleep 1
|
||||
|
||||
# ── Check 4: Verify /dev/kvm now exists ──────────────────────
|
||||
if [ ! -e /dev/kvm ]; then
|
||||
printf 'KVM_FAIL:no_dev_kvm (modules may not have loaded -- dmesg for details)\n'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Check 5: Verify /dev/kvm is a char device ────────────────
|
||||
if [ ! -c /dev/kvm ]; then
|
||||
printf 'KVM_FAIL:dev_kvm_not_char_device\n'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Success ──────────────────────────────────────────────────
|
||||
printf 'KVM_OK\n'
|
||||
exit 0
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/bin/sh
|
||||
# ============================================================
|
||||
# log_flush.sh -- Async ring-buffer to USB flush daemon
|
||||
#
|
||||
# Runs in the background, calling sync() on the log directory
|
||||
# every 5 seconds so boot logs survive even on a hard reset.
|
||||
# Writes a periodic heartbeat so agents can detect hung sessions.
|
||||
# Exits cleanly when the /tmp/hiperiso_done sentinel appears,
|
||||
# writing a final flush marker.
|
||||
#
|
||||
# Usage: log_flush.sh <log_dir>
|
||||
# ============================================================
|
||||
|
||||
LOG_DIR="${1:-}"
|
||||
INTERVAL=5
|
||||
SENTINEL="/tmp/hiperiso_done"
|
||||
HEARTBEAT_INTERVAL=12
|
||||
|
||||
if [ -z "$LOG_DIR" ]; then
|
||||
printf '[log_flush] FATAL: no log directory specified\n'
|
||||
printf '[log_flush] usage: log_flush.sh <log_dir>\n'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$LOG_DIR" ]; then
|
||||
printf '[log_flush] FATAL: log directory does not exist: %s\n' "$LOG_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '[log_flush] Started -- syncing %s every %ds\n' "$LOG_DIR" "$INTERVAL"
|
||||
|
||||
_flush_count=0
|
||||
|
||||
while true; do
|
||||
# ── Check sentinel: exit gracefully ──────────────────────
|
||||
if [ -f "$SENTINEL" ]; then
|
||||
printf '[log_flush] Sentinel detected, performing final sync...\n'
|
||||
sync
|
||||
printf '[log_flush] Exiting after %d sync cycles\n' "$_flush_count"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Check if log directory still accessible ──────────────
|
||||
# (USB might have been unplugged)
|
||||
if [ ! -d "$LOG_DIR" ]; then
|
||||
printf '[log_flush] WARNING: log dir vanished (USB unplugged?) -- waiting for sentinel\n'
|
||||
sleep "$INTERVAL"
|
||||
continue
|
||||
fi
|
||||
|
||||
# ── Flush dirty pages to the USB device ──────────────────
|
||||
sync 2>/dev/null
|
||||
|
||||
_flush_count=$(( _flush_count + 1 ))
|
||||
|
||||
# ── Write periodic heartbeat ─────────────────────────────
|
||||
if [ $(( _flush_count % HEARTBEAT_INTERVAL )) -eq 0 ]; then
|
||||
_up=$(cut -d' ' -f1 /proc/uptime 2>/dev/null)
|
||||
printf 'cycle=%d uptime=%s\n' "$_flush_count" "${_up:-0}" \
|
||||
> "${LOG_DIR}/HEARTBEAT" 2>/dev/null
|
||||
printf '[log_flush] cycle %d (%d min) -- OK\n' \
|
||||
"$_flush_count" "$(( _flush_count / HEARTBEAT_INTERVAL ))"
|
||||
fi
|
||||
|
||||
sleep "$INTERVAL"
|
||||
done
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/bin/sh
|
||||
# make_floppy.sh -- Create a virtual floppy disk image with given files
|
||||
# Usage: make_floppy.sh <output.img> <file1> [file2] ...
|
||||
# POSIX sh compatible (busybox/ash/dash)
|
||||
|
||||
set -eu
|
||||
|
||||
OUTPUT="${1:-}"
|
||||
shift || true
|
||||
|
||||
if [ -z "$OUTPUT" ] || [ $# -eq 0 ]; then
|
||||
echo "Usage: make_floppy.sh <output.img> <file1> [file2] ..." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
. /hiperiso-lib.sh
|
||||
|
||||
_total_size=0
|
||||
for f in "$@"; do
|
||||
if [ -f "$f" ]; then
|
||||
_sz=$(wc -c < "$f" 2>/dev/null || echo 0)
|
||||
_total_size=$((_total_size + _sz))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$_total_size" -gt 2621440 ]; then
|
||||
_floppy_size=2880
|
||||
else
|
||||
_floppy_size=1440
|
||||
fi
|
||||
|
||||
dd if=/dev/zero of="$OUTPUT" bs=1024 count=$_floppy_size 2>/dev/null
|
||||
|
||||
if command -v mkfs.vfat >/dev/null 2>&1; then
|
||||
mkfs.vfat -F 12 -n HIPERISO "$OUTPUT" >/dev/null 2>&1
|
||||
elif command -v mkdosfs >/dev/null 2>&1; then
|
||||
mkdosfs -F 12 -n HIPERISO "$OUTPUT" >/dev/null 2>&1
|
||||
else
|
||||
hiperiso_log "WARNING: no FAT formatter available, floppy image is blank"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
_mnt=$(mktemp -d /tmp/floppy.XXXXXX 2>/dev/null || echo /tmp/floppy_mnt)
|
||||
mkdir -p "$_mnt"
|
||||
|
||||
if mount -o loop "$OUTPUT" "$_mnt" 2>/dev/null; then
|
||||
for f in "$@"; do
|
||||
if [ -f "$f" ]; then
|
||||
cp "$f" "$_mnt/" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
sync
|
||||
umount "$_mnt" 2>/dev/null || true
|
||||
else
|
||||
if command -v mcopy >/dev/null 2>&1; then
|
||||
for f in "$@"; do
|
||||
if [ -f "$f" ]; then
|
||||
mcopy -i "$OUTPUT" "$f" ::/ 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
else
|
||||
hiperiso_log "WARNING: cannot mount loop or use mtools, floppy is empty"
|
||||
fi
|
||||
fi
|
||||
|
||||
rmdir "$_mnt" 2>/dev/null || true
|
||||
exit 0
|
||||
Executable
+296
@@ -0,0 +1,296 @@
|
||||
#!/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"
|
||||
@@ -0,0 +1,248 @@
|
||||
#
|
||||
# hiperiso host kernel configuration
|
||||
# ================================
|
||||
# Minimal x86_64 Linux kernel for the hiperiso hypervisor host.
|
||||
#
|
||||
# Goals:
|
||||
# - KVM built-in (NOT module) so /dev/kvm is available with no module loader
|
||||
# - USB mass-storage support (XHCI/EHCI/OHCI/UHCI) to read the USB stick
|
||||
# - exFAT / NTFS3 / ext4 / FAT filesystems to read ISOs and write logs
|
||||
# - Everything else stripped: no networking, no modules, no sound/wifi/DRM
|
||||
# - Small enough to bzImage in ~5-8 MB
|
||||
#
|
||||
# Apply with: cp hiperiso_defconfig .config && make olddefconfig
|
||||
#
|
||||
|
||||
# ── Architecture ────────────────────────────────────────────────────────────
|
||||
CONFIG_64BIT=y
|
||||
CONFIG_X86_64=y
|
||||
CONFIG_X86=y
|
||||
CONFIG_GENERIC_CPU=y
|
||||
CONFIG_SMP=y
|
||||
CONFIG_NR_CPUS=64
|
||||
CONFIG_X86_LOCAL_APIC=y
|
||||
CONFIG_X86_IO_APIC=y
|
||||
CONFIG_HPET_TIMER=y
|
||||
|
||||
# ── Local version / hostname ────────────────────────────────────────────────
|
||||
CONFIG_LOCALVERSION="-hiperiso"
|
||||
CONFIG_DEFAULT_HOSTNAME="hiperiso"
|
||||
|
||||
# ── Kernel timing / scheduler (KVM depends on HIGH_RES_TIMERS) ──────────────
|
||||
CONFIG_HIGH_RES_TIMERS=y
|
||||
CONFIG_TICK_ONESHOT=y
|
||||
CONFIG_NO_HZ_IDLE=y
|
||||
CONFIG_PREEMPT_VOLUNTARY=y
|
||||
|
||||
# ── Virtualization: KVM built-in (NOT module) ───────────────────────────────
|
||||
CONFIG_VIRTUALIZATION=y
|
||||
CONFIG_KVM=y
|
||||
CONFIG_KVM_INTEL=y
|
||||
CONFIG_KVM_AMD=y
|
||||
CONFIG_HAVE_KVM=y
|
||||
CONFIG_HAVE_KVM_IRQCHIP=y
|
||||
CONFIG_HAVE_KVM_IRQ_ROUTING=y
|
||||
CONFIG_HAVE_KVM_EVENTFD=y
|
||||
CONFIG_IRQ_BYPASS_MANAGER=y
|
||||
CONFIG_USER_RETURN_NOTIFIER=y
|
||||
CONFIG_VIRTUALIZATION_HOST=y
|
||||
|
||||
# ── Modules: disabled (everything built-in) ────────────────────────────────
|
||||
# CONFIG_MODULES is not set
|
||||
# CONFIG_MODULE_SIG is not set
|
||||
|
||||
# ── Networking: disabled (host needs no network) ────────────────────────────
|
||||
# CONFIG_NET is not set
|
||||
# CONFIG_WIRELESS is not set
|
||||
# CONFIG_RFKILL is not set
|
||||
# CONFIG_NET_9P is not set
|
||||
|
||||
# ── PCI (host USB controllers live on PCI/PCIe) ─────────────────────────────
|
||||
CONFIG_PCI=y
|
||||
CONFIG_PCIEPORTBUS=y
|
||||
CONFIG_PCI_MSI=y
|
||||
CONFIG_PCI_IOV=y
|
||||
# CONFIG_PCIEASPM is not set
|
||||
# CONFIG_PCI_HISI is not set
|
||||
|
||||
# ── AHCI / SATA (storage controllers on real hardware) ──────────────────────
|
||||
CONFIG_ATA=y
|
||||
CONFIG_SATA_AHCI=y
|
||||
CONFIG_ATA_PIIX=y
|
||||
|
||||
# ── Virtio (paravirtualized devices for KVM/QEMU guests) ────────────────────
|
||||
CONFIG_VIRTIO=y
|
||||
CONFIG_VIRTIO_PCI=y
|
||||
CONFIG_VIRTIO_BLK=y
|
||||
CONFIG_VIRTIO_CONSOLE=y
|
||||
|
||||
# ── ACPI (host firmware enumeration + power) ────────────────────────────────
|
||||
CONFIG_ACPI=y
|
||||
CONFIG_ACPI_AC=y
|
||||
CONFIG_ACPI_BUTTON=y
|
||||
CONFIG_ACPI_FAN=y
|
||||
CONFIG_ACPI_THERMAL=y
|
||||
CONFIG_ACPI_TABLE_UPGRADE=y
|
||||
# CONFIG_ACPI_DEBUG is not set
|
||||
|
||||
# ── USB support (read ISOs/logs from the USB stick) ─────────────────────────
|
||||
CONFIG_USB_SUPPORT=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
|
||||
CONFIG_USB_XHCI_HCD=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_EHCI_HCD_PLATFORM=y
|
||||
CONFIG_USB_OHCI_HCD=y
|
||||
CONFIG_USB_OHCI_HCD_PCI=y
|
||||
CONFIG_USB_UHCI_HCD=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB_STORAGE_REALTEK=y
|
||||
CONFIG_USB_STORAGE_DATAFAB=y
|
||||
CONFIG_USB_DEFAULT_PERSIST=y
|
||||
|
||||
# ── HID (USB keyboards/mice) ─────────────────────────────────────────────────
|
||||
CONFIG_HID=y
|
||||
CONFIG_HID_GENERIC=y
|
||||
CONFIG_USB_HID=y
|
||||
|
||||
# ── SCSI / block layer (USB mass-storage is a SCSI host) ────────────────────
|
||||
CONFIG_BLOCK=y
|
||||
CONFIG_BLK_DEV=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
CONFIG_CHR_DEV_SG=y
|
||||
CONFIG_SCSI_CONSTANTS=y
|
||||
CONFIG_SCSI_SCAN_ASYNC=y
|
||||
|
||||
# ── Filesystems ─────────────────────────────────────────────────────────────
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_EXT4_FS_POSIX_ACL=y
|
||||
CONFIG_EXT4_FS_SECURITY=y
|
||||
CONFIG_FAT_FS=y
|
||||
CONFIG_MSDOS_FS=y
|
||||
CONFIG_VFAT_FS=y
|
||||
CONFIG_FAT_DEFAULT_CODEPAGE=437
|
||||
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
|
||||
CONFIG_FAT_DEFAULT_UTF8=y
|
||||
CONFIG_EXFAT_FS=y
|
||||
CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8"
|
||||
CONFIG_NTFS3_FS=y
|
||||
CONFIG_NTFS3_64BIT_CLUSTER=y
|
||||
CONFIG_NTFS3_LZX_XPRESS=y
|
||||
CONFIG_NTFS3_FS_POSIX_ACL=y
|
||||
# CONFIG_NTFS_FS is not set
|
||||
CONFIG_PROC_FS=y
|
||||
CONFIG_PROC_SYSCTL=y
|
||||
CONFIG_SYSFS=y
|
||||
CONFIG_TMPFS=y
|
||||
CONFIG_TMPFS_POSIX_ACL=y
|
||||
CONFIG_TMPFS_XATTR=y
|
||||
CONFIG_HUGETLBFS=y
|
||||
CONFIG_HUGETLB_PAGE=y
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
CONFIG_CONFIGFS_FS=y
|
||||
|
||||
# ── Initramfs (supplied externally as initramfs.cpio.gz) ────────────────────
|
||||
CONFIG_BLK_DEV_INITRD=y
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
CONFIG_RD_GZIP=y
|
||||
CONFIG_DECOMPRESS_GZIP=y
|
||||
|
||||
# ── Binary formats ──────────────────────────────────────────────────────────
|
||||
CONFIG_BINFMT_ELF=y
|
||||
CONFIG_BINFMT_SCRIPT=y
|
||||
CONFIG_BINFMT_MISC=y
|
||||
|
||||
# ── Console / TTY / serial (host console + early debug) ─────────────────────
|
||||
CONFIG_TTY=y
|
||||
CONFIG_VT=y
|
||||
CONFIG_VT_CONSOLE=y
|
||||
CONFIG_HW_CONSOLE=y
|
||||
CONFIG_UNIX98_PTYS=y
|
||||
CONFIG_LEGACY_PTYS=y
|
||||
CONFIG_SERIAL_8250=y
|
||||
CONFIG_SERIAL_8250_CONSOLE=y
|
||||
CONFIG_SERIAL_8250_PCI=y
|
||||
CONFIG_SERIAL_8250_NR_UARTS=4
|
||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
|
||||
CONFIG_PRINTK=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_EARLY_PRINTK=y
|
||||
CONFIG_DEBUG_INFO_NONE=y
|
||||
|
||||
# CONFIG_EFI is not set
|
||||
# CONFIG_EFI_STUB is not set
|
||||
|
||||
# ── Kernel features / syscalls (busybox + glibc needs) ──────────────────────
|
||||
CONFIG_MULTIUSER=y
|
||||
CONFIG_FHANDLE=y
|
||||
CONFIG_POSIX_TIMERS=y
|
||||
CONFIG_FUTEX=y
|
||||
CONFIG_EPOLL=y
|
||||
CONFIG_SIGNALFD=y
|
||||
CONFIG_TIMERFD=y
|
||||
CONFIG_EVENTFD=y
|
||||
CONFIG_AIO=y
|
||||
CONFIG_IO_URING=y
|
||||
CONFIG_INOTIFY_USER=y
|
||||
CONFIG_FANOTIFY=y
|
||||
CONFIG_ADVISE_SYSCALLS=y
|
||||
CONFIG_MEMBARRIER=y
|
||||
CONFIG_KALLSYMS=y
|
||||
CONFIG_BUG=y
|
||||
CONFIG_ELF_CORE=y
|
||||
CONFIG_BASE_FULL=y
|
||||
CONFIG_SGETMASK_SYSCALL=y
|
||||
CONFIG_KCMP=y
|
||||
CONFIG_STACKTRACE_SUPPORT=y
|
||||
CONFIG_HAVE_KERNEL_GZIP=y
|
||||
CONFIG_KERNEL_GZIP=y
|
||||
|
||||
# ── Crypto (QEMU/KVM + TLS needs a few algos; keep minimal built-in) ────────
|
||||
CONFIG_CRYPTO=y
|
||||
CONFIG_CRYPTO_CRC32C=y
|
||||
CONFIG_CRYPTO_SHA256=y
|
||||
CONFIG_CRYPTO_AES=y
|
||||
CONFIG_CRYPTO_GCM=y
|
||||
|
||||
# ── Input (keyboard for VT console) ─────────────────────────────────────────
|
||||
CONFIG_INPUT=y
|
||||
CONFIG_INPUT_KEYBOARD=y
|
||||
CONFIG_KEYBOARD_ATKBD=y
|
||||
CONFIG_INPUT_MISC=y
|
||||
|
||||
# ── RTC / timekeeping ───────────────────────────────────────────────────────
|
||||
CONFIG_RTC_LIB=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_HCTOSYS=y
|
||||
|
||||
# ── Explicitly stripped (size reduction) ────────────────────────────────────
|
||||
# CONFIG_SOUND is not set
|
||||
# CONFIG_SND is not set
|
||||
# CONFIG_DRM is not set
|
||||
# CONFIG_BT is not set
|
||||
# CONFIG_MEDIA_SUPPORT is not set
|
||||
# CONFIG_WATCHDOG is not set
|
||||
# CONFIG_AUDIT is not set
|
||||
# CONFIG_SECURITY is not set
|
||||
# CONFIG_SECURITYFS is not set
|
||||
# CONFIG_QUOTA is not set
|
||||
# CONFIG_SWAP is not set
|
||||
# CONFIG_SUSPEND is not set
|
||||
# CONFIG_HIBERNATION is not set
|
||||
# CONFIG_CPU_FREQ is not set
|
||||
# CONFIG_CPU_IDLE is not set
|
||||
# CONFIG_WLAN is not set
|
||||
# CONFIG_NLATTR is not set
|
||||
# CONFIG_COMPAT_BRK is not set
|
||||
|
||||
# ── Debug (lightweight) ─────────────────────────────────────────────────────
|
||||
# CONFIG_DEBUG_INFO is not set
|
||||
# CONFIG_KASAN is not set
|
||||
# CONFIG_UBSAN is not set
|
||||
CONFIG_PANIC_ON_OOPS=y
|
||||
CONFIG_PANIC_TIMEOUT=5
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_SCHED_DEBUG=y
|
||||
Reference in New Issue
Block a user