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:
2026-06-30 14:29:01 +03:00
parent 9aac84e699
commit 4325590686
11 changed files with 2400 additions and 2 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
# Build artifacts
build/
grub2/
/build/
/grub2/
*.o
*.obj
*.mod
+63
View File
@@ -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
+94
View File
@@ -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
+706
View File
@@ -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
}
+506
View File
@@ -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
+270
View File
@@ -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
+82
View File
@@ -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
+66
View File
@@ -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
+67
View File
@@ -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
+296
View File
@@ -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"
+248
View File
@@ -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