#!/bin/bash
# Autopkgtest: boot a kernel under cloud-hypervisor and verify it reaches
# the expected VFS panic (no rootfs supplied).  This proves the VMM binary
# is functional and can drive KVM to execute a real kernel.

set -eu

SERIAL_LOG="$(mktemp)"
CLEANUP_FILES="$SERIAL_LOG"
cleanup() { rm -f $CLEANUP_FILES; }
trap cleanup EXIT

# Pick the running kernel so we don't need extra packages.
KERNEL="/boot/vmlinuz-$(uname -r)"
if [ ! -r "$KERNEL" ]; then
    echo "SKIP: kernel image $KERNEL not readable" >&2
    exit 77
fi

# Verify /dev/kvm is usable.
modprobe kvm || true
if [ ! -w /dev/kvm ]; then
    echo "SKIP: /dev/kvm not writable" >&2
    exit 77
fi

# Architecture-specific console device and kernel preparation.
ARCH="$(dpkg --print-architecture)"
case "$ARCH" in
    amd64)   CONSOLE_DEV=ttyS0 ;;
    arm64)   CONSOLE_DEV=ttyAMA0 ;;
    riscv64)
        CONSOLE_DEV=ttyS0

        # RISC-V KVM requires AIA (Advanced Interrupt Architecture)
        # extensions.  Skip the test when the CPU does not advertise them.
        ISA="$(grep -m1 -oP '^isa\s*:\s*\K.*' /proc/cpuinfo || true)"
        if ! echo "$ISA" | grep -qi 'ssaia'; then
            echo "SKIP: CPU does not advertise ssaia; KVM AIA unavailable"
            exit 77
        fi

        # Ubuntu riscv64 kernels are zboot images (zstd-compressed
        # inside a PE stub).  cloud-hypervisor's PE loader cannot
        # handle them, so decompress to a plain Image first.
        if file "$KERNEL" | grep -q 'zboot\|zstd compressed'; then
            EXTRACT="/usr/src/linux-headers-$(uname -r)/scripts/extract-vmlinux"
            if [ ! -x "$EXTRACT" ]; then
                echo "FAIL: $EXTRACT not found; install linux-headers-$(uname -r)" >&2
                exit 1
            fi
            DECOMPRESSED="$(mktemp)"
            CLEANUP_FILES="$CLEANUP_FILES $DECOMPRESSED"
            echo "Decompressing zboot kernel with extract-vmlinux..."
            "$EXTRACT" "$KERNEL" > "$DECOMPRESSED"
            KERNEL="$DECOMPRESSED"
        fi
        ;;
    *)      echo "FAIL: unsupported architecture $ARCH"; exit 1 ;;
esac

echo "Booting $KERNEL under cloud-hypervisor (arch=$ARCH, console=$CONSOLE_DEV)..."

# Exit code 0   = CHV exited normally (guest shutdown/triple-fault)
# Exit code 124 = timeout fired
# Other         = real failure
rc=0
timeout 15 cloud-hypervisor \
    --kernel "$KERNEL" \
    --cmdline "console=$CONSOLE_DEV panic=0" \
    --serial "file=$SERIAL_LOG" \
    --console off \
    --memory size=256M \
    || rc=$?

echo "cloud-hypervisor exited with code $rc"

# Both 0 (clean exit / triple-fault) and 124 (timeout) are acceptable as
# long as the serial log proves the kernel actually booted.
if [ "$rc" -ne 0 ] && [ "$rc" -ne 124 ]; then
    echo "FAIL: unexpected cloud-hypervisor exit code $rc" >&2
    echo "--- serial log ---"
    cat "$SERIAL_LOG"
    echo "--- end serial log ---"
    exit 1
fi

echo "--- serial log (last 30 lines) ---"
tail -30 "$SERIAL_LOG"
echo "--- end serial log ---"

# Check for evidence that the kernel actually ran.
if grep -q "Linux version" "$SERIAL_LOG"; then
    echo "PASS: kernel booted successfully under cloud-hypervisor"
else
    echo "FAIL: 'Linux version' not found in serial log" >&2
    echo "--- full serial log ---"
    cat "$SERIAL_LOG"
    echo "--- end full serial log ---"
    exit 1
fi
