#!/bin/bash
#
# ch7-verify.sh
#
# PiLFS Chapter 7 post-build verification.
# Run INSIDE the PiLFS chroot after ch7-build.sh completes.
#
# IMPORTANT:
# During Chapter 7 the Raspberry Pi OS host still boots from p2.
# The PiLFS kernel modules and Broadcom userspace should be installed
# into the PiLFS root, but the shared boot partition's active host
# kernel should NOT be overwritten yet.
#

set -u

BOOTDEV=/dev/mmcblk0p1
BOOTMNT=/boot
HOST_ROOT_DEV=/dev/mmcblk0p2
PILFS_ROOT_DEV=/dev/mmcblk0p3
FIRMWARE_SRC=/sources/firmware-master

PASS=0
WARN=0
FAIL=0
BOOT_MOUNTED_BY_SCRIPT=0

pass() {
    printf 'PASS: %s\n' "$*"
    PASS=$((PASS + 1))
}

warn() {
    printf 'WARN: %s\n' "$*"
    WARN=$((WARN + 1))
}

fail() {
    printf 'FAIL: %s\n' "$*"
    FAIL=$((FAIL + 1))
}

section() {
    echo
    echo "============================================================"
    echo "$1"
    echo "============================================================"
}

cleanup() {
    if [ "$BOOT_MOUNTED_BY_SCRIPT" -eq 1 ]; then
        echo
        echo "Unmounting $BOOTMNT..."
        if umount "$BOOTMNT"; then
            echo "Unmount complete."
        else
            echo "WARNING: Could not unmount $BOOTMNT."
        fi
    fi
}
trap cleanup EXIT

section "PiLFS Chapter 7 Verification"
echo "This script expects to be running INSIDE the PiLFS chroot."
echo
echo "Expected disk layout:"
echo "  $BOOTDEV       = shared boot partition"
echo "  $HOST_ROOT_DEV = host Raspberry Pi OS"
echo "  $PILFS_ROOT_DEV = PiLFS root"
echo
echo "Chapter 7 policy:"
echo "  - Install Broadcom userspace into PiLFS"
echo "  - Install PiLFS kernel modules into /lib/modules"
echo "  - DO NOT overwrite the host kernel on the shared boot partition yet"

section "1. Environment"

if [ "$(id -u)" -eq 0 ]; then
    pass "Running as root"
else
    fail "This script must be run as root"
fi

if [ -d "$FIRMWARE_SRC" ]; then
    pass "Firmware source directory exists: $FIRMWARE_SRC"
else
    fail "Firmware source directory missing: $FIRMWARE_SRC"
fi

if [ -r /proc/1/root ] || [ -d /proc ]; then
    pass "/proc is available inside chroot"
else
    fail "/proc is not available"
fi

section "2. Boot Partition"

if [ -b "$BOOTDEV" ]; then
    pass "$BOOTDEV exists as a block device"
else
    fail "$BOOTDEV is not available as a block device"
fi

if mountpoint -q "$BOOTMNT"; then
    pass "$BOOTMNT is already mounted"
else
    echo "Mounting $BOOTDEV on $BOOTMNT..."
    if mount "$BOOTDEV" "$BOOTMNT"; then
        BOOT_MOUNTED_BY_SCRIPT=1
        pass "Mounted $BOOTDEV on $BOOTMNT"
    else
        fail "Could not mount $BOOTDEV on $BOOTMNT"
    fi
fi

BOOT_SOURCE="$(findmnt -n -o SOURCE "$BOOTMNT" 2>/dev/null || true)"
if [ "$BOOT_SOURCE" = "$BOOTDEV" ]; then
    pass "Confirmed actual boot filesystem is $BOOTDEV"
else
    fail "$BOOTMNT source is '$BOOT_SOURCE', expected '$BOOTDEV'"
fi

section "3. Shared Boot Kernel Preservation"

RUNNING_KERNEL="$(uname -r)"
echo "Currently running host kernel: $RUNNING_KERNEL"

if [[ "$RUNNING_KERNEL" == *"6.1.45-v8+"* ]]; then
    warn "Running kernel looks like the PiLFS firmware kernel ($RUNNING_KERNEL)"
    echo "      This may indicate the shared host boot kernel was already overwritten."
else
    pass "Running kernel does not look like the PiLFS 6.1.45-v8+ kernel"
fi

for image in kernel.img kernel7.img kernel7l.img kernel8.img; do
    if [ -s "$BOOTMNT/$image" ]; then
        pass "$image exists and is non-empty on the shared boot partition"
    else
        warn "$image is missing or empty on the shared boot partition"
    fi
done

if [ -s "$FIRMWARE_SRC/boot/kernel8.img" ] && [ -s "$BOOTMNT/kernel8.img" ]; then
    SRC_HASH="$(sha256sum "$FIRMWARE_SRC/boot/kernel8.img" | awk '{print $1}')"
    BOOT_HASH="$(sha256sum "$BOOTMNT/kernel8.img" | awk '{print $1}')"

    echo
    echo "--- kernel8.img preservation comparison ---"
    echo "PiLFS source: $SRC_HASH"
    echo "Shared boot:  $BOOT_HASH"

    if [ "$SRC_HASH" != "$BOOT_HASH" ]; then
        pass "Shared kernel8.img was NOT overwritten by the PiLFS source kernel"
    else
        fail "Shared kernel8.img exactly matches the PiLFS source kernel"
        echo "      Chapter 7 should answer NO to the bootloader/kernel overwrite prompt."
    fi
else
    warn "Could not compare PiLFS source kernel8.img with shared boot kernel8.img"
fi

if [ -s "$BOOTMNT/kernel_2712.img" ]; then
    pass "kernel_2712.img is present on the shared boot partition"
fi

section "4. Device Trees on Shared Boot Partition"

DTB_COUNT="$(find "$BOOTMNT" -maxdepth 1 -type f -name '*.dtb' 2>/dev/null | wc -l)"
if [ "$DTB_COUNT" -gt 0 ]; then
    pass "Found $DTB_COUNT device-tree files on shared boot partition"
else
    fail "No device-tree files found on shared boot partition"
fi

echo
echo "BCM271x device trees:"
find "$BOOTMNT" -maxdepth 1 -type f -name 'bcm271*.dtb' -printf '  %f\n' 2>/dev/null | sort || true

section "5. Device Tree Overlays"

if [ -d "$BOOTMNT/overlays" ]; then
    OVERLAY_COUNT="$(find "$BOOTMNT/overlays" -maxdepth 1 -type f 2>/dev/null | wc -l)"
    if [ "$OVERLAY_COUNT" -gt 0 ]; then
        pass "Overlay directory contains $OVERLAY_COUNT files"
    else
        fail "Overlay directory exists but contains no files"
    fi
else
    fail "$BOOTMNT/overlays does not exist"
fi

section "6. Raspberry Pi Firmware Files on Shared Boot Partition"

for f in start.elf start_x.elf start4.elf fixup.dat fixup_x.dat fixup4.dat; do
    if [ -s "$BOOTMNT/$f" ]; then
        pass "$f present"
    else
        warn "$f missing"
    fi
done

section "7. PiLFS Kernel Modules"

if [ -d /lib/modules ]; then
    pass "/lib/modules exists"
else
    fail "/lib/modules does not exist"
fi

echo
echo "Installed module trees:"
find /lib/modules -mindepth 1 -maxdepth 1 -type d -printf '  %f\n' 2>/dev/null | sort || true

V8_TREE="$(find /lib/modules -mindepth 1 -maxdepth 1 -type d -name '*-v8+' -printf '%f\n' 2>/dev/null | head -1)"

if [ -n "$V8_TREE" ]; then
    pass "64-bit Raspberry Pi v8 module tree found: $V8_TREE"

    echo
    echo "Checking /lib/modules/$V8_TREE"

    MODULE_COUNT="$(find "/lib/modules/$V8_TREE" -type f \( -name '*.ko' -o -name '*.ko.xz' -o -name '*.ko.zst' \) 2>/dev/null | wc -l)"
    if [ "$MODULE_COUNT" -gt 0 ]; then
        pass "$V8_TREE contains $MODULE_COUNT kernel modules"
    else
        fail "$V8_TREE contains no kernel modules"
    fi

    for f in modules.dep modules.alias modules.builtin; do
        if [ -s "/lib/modules/$V8_TREE/$f" ]; then
            pass "$V8_TREE/$f exists"
        else
            fail "$V8_TREE/$f is missing"
        fi
    done
else
    fail "No Raspberry Pi *-v8+ module tree found in /lib/modules"
fi

section "8. Firmware Module Source Comparison"

if [ -d "$FIRMWARE_SRC/modules" ]; then
    echo "Module trees contained in firmware source:"
    mapfile -t SOURCE_TREES < <(find "$FIRMWARE_SRC/modules" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort)
    printf '  %s\n' "${SOURCE_TREES[@]}"

    for tree in "${SOURCE_TREES[@]}"; do
        if [ -d "/lib/modules/$tree" ]; then
            pass "Source module tree $tree was installed"
        else
            fail "Source module tree $tree is missing from /lib/modules"
        fi
    done
else
    fail "Firmware source modules directory is missing"
fi

section "9. Broadcom /opt/vc"

if [ -d /opt/vc ]; then
    pass "/opt/vc exists"
else
    fail "/opt/vc does not exist"
fi

if [ -d /opt/vc/bin ]; then
    VC_BIN_COUNT="$(find /opt/vc/bin -maxdepth 1 -type f 2>/dev/null | wc -l)"
    pass "/opt/vc/bin exists with $VC_BIN_COUNT files"
else
    fail "/opt/vc/bin does not exist"
fi

if [ -d /opt/vc/lib ]; then
    VC_LIB_COUNT="$(find /opt/vc/lib -maxdepth 1 -type f 2>/dev/null | wc -l)"
    pass "/opt/vc/lib exists with $VC_LIB_COUNT files"
else
    fail "/opt/vc/lib does not exist"
fi

section "10. Broadcom Library Configuration"

if [ -f /etc/ld.so.conf.d/broadcom.conf ]; then
    pass "/etc/ld.so.conf.d/broadcom.conf exists"
    echo
    cat /etc/ld.so.conf.d/broadcom.conf
    if grep -qx '/opt/vc/lib' /etc/ld.so.conf.d/broadcom.conf; then
        pass "/opt/vc/lib is listed in Broadcom ld.so configuration"
    else
        fail "/opt/vc/lib is not listed in broadcom.conf"
    fi
else
    fail "/etc/ld.so.conf.d/broadcom.conf does not exist"
fi

section "11. PiLFS Userspace Architecture"

if [ -x /bin/bash ]; then
    BASH_INFO="$(file /bin/bash)"
    echo "$BASH_INFO"
    if echo "$BASH_INFO" | grep -qiE 'aarch64|ARM aarch64|ARM64'; then
        pass "/bin/bash appears to be 64-bit ARM/AArch64"
    else
        fail "/bin/bash does not appear to be AArch64"
    fi
else
    fail "/bin/bash is missing"
fi

section "12. Current cmdline.txt"

if [ -f "$BOOTMNT/cmdline.txt" ]; then
    echo
    cat "$BOOTMNT/cmdline.txt"
    echo

    ROOT_ARG="$(grep -oE 'root=[^ ]+' "$BOOTMNT/cmdline.txt" | head -1 || true)"
    echo "Current kernel root argument: ${ROOT_ARG:-<none>}"

    if [ "$ROOT_ARG" = "root=$HOST_ROOT_DEV" ]; then
        pass "cmdline.txt still points to host OS p2"
        echo "      This is EXPECTED while PiLFS is still being built."
    elif [ "$ROOT_ARG" = "root=$PILFS_ROOT_DEV" ]; then
        warn "cmdline.txt already points to PiLFS p3"
        echo "      Only do this when the PiLFS system is ready for final boot."
    else
        fail "Unexpected or missing root= argument in cmdline.txt"
    fi
else
    fail "cmdline.txt does not exist"
fi

section "13. Current config.txt"

if [ -f "$BOOTMNT/config.txt" ]; then
    pass "config.txt exists"
    echo
    echo "Relevant config.txt entries:"
    grep -E '^(kernel=|arm_64bit=|dtoverlay=|dtparam=)' "$BOOTMNT/config.txt" 2>/dev/null || true

    if grep -q '^kernel=.*lfs' "$BOOTMNT/config.txt" 2>/dev/null; then
        warn "config.txt already selects an LFS-specific kernel"
        echo "      Keep the host kernel selected until the final PiLFS cutover."
    else
        pass "config.txt does not explicitly select an LFS-specific kernel"
    fi
else
    fail "config.txt does not exist"
fi

section "14. Host/PiLFS Kernel Separation Check"

if [ -n "$V8_TREE" ]; then
    echo "Running host kernel: $RUNNING_KERNEL"
    echo "PiLFS v8 modules:     $V8_TREE"

    if [ "$RUNNING_KERNEL" != "$V8_TREE" ]; then
        pass "Host kernel and PiLFS module tree are intentionally separate during the build"
    else
        warn "Running host kernel exactly matches the PiLFS module tree"
        echo "      Verify that the shared boot kernel has not been overwritten."
    fi
fi

section "FINAL RESULT"

echo
echo "PASS: $PASS"
echo "WARN: $WARN"
echo "FAIL: $FAIL"

echo
if [ "$FAIL" -eq 0 ]; then
    section "CHAPTER 7 SANITY CHECK PASSED"
    echo
    echo "No critical Chapter 7 copy/install failures were detected."
    echo
    echo "The PiLFS firmware/modules/userspace are present, and the"
    echo "shared boot partition has not been required to use the PiLFS"
    echo "kernel yet."
    echo
    echo "NOTE:"
    echo "This does NOT mean PiLFS is ready to boot yet."
    echo "cmdline.txt should normally still point to mmcblk0p2."
    exit 0
else
    section "CHAPTER 7 SANITY CHECK FAILED"
    echo
    echo "One or more critical checks failed."
    echo "Do not continue until the FAIL entries are understood."
    exit 1
fi
