summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-13 18:36:21 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-13 18:36:21 -0400
commit00e54c13c7c9030c4d5f7151467f2d85a3730e29 (patch)
tree17a45239df8d2e4912ff3697ed06e85a10b969de /scripts
downloadopen-iov-00e54c13c7c9030c4d5f7151467f2d85a3730e29.tar.gz
open-iov-00e54c13c7c9030c4d5f7151467f2d85a3730e29.zip
Initial commit: Open-IOV vGPU-IO complete stack
- Firmware decompilation: GSP 570.144W (28.5MB ELF, RV64IMAC), 132 decompiled C files - Kernel modules: nvidia.ko (34MB, OpenRM), open-iov-vfio.ko (2.8MB, VFIO mdev), gsp-vgpu.ko (1.8MB, guest driver) - Userspace: libiov.so/libvgpu.so (76KB), example_vgpu (28KB), rpc_emulator.c - PCI config space emulation (256B with PM, MSI, MSI-X, PCIe caps) - 12 RPC types fully mapped from firmware decompilation - BAR trap, WPR, scheduling, notification, migration emulation - Helper scripts: load_vgpu_stack, create_mdev, launch_qemu_vgpu, test_vgpu_stack, setup_guest_driver, unload_vgpu_stack - Architecture documentation - Wiki: 10 technical documents - Git LFS for large binaries (.ko, .bin, .iso, .img, .o, ghidra repos) - nvidia-open-gpu-kernel-modules as submodule
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/create_mdev.sh86
-rwxr-xr-xscripts/launch_qemu_vgpu.sh183
-rwxr-xr-xscripts/load_vgpu_stack.sh65
-rwxr-xr-xscripts/setup_guest_driver.sh99
-rwxr-xr-xscripts/test_vgpu_stack.sh127
-rwxr-xr-xscripts/unload_vgpu_stack.sh36
6 files changed, 596 insertions, 0 deletions
diff --git a/scripts/create_mdev.sh b/scripts/create_mdev.sh
new file mode 100755
index 0000000..f6c76ed
--- /dev/null
+++ b/scripts/create_mdev.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+# create_mdev.sh - Create NVIDIA vGPU mediated device for QEMU
+# Usage: ./create_mdev.sh [PCI_ADDRESS] [VGPU_TYPE]
+
+set -euo pipefail
+
+PCI_ADDR="${1:-}"
+VGPU_TYPE="${2:-nvidia-vgpu-turing}"
+
+# Supported vGPU types
+VALID_TYPES=("nvidia-vgpu-ampere" "nvidia-vgpu-hopper" "nvidia-vgpu-turing" "nvidia-vgpu-pascal")
+
+# Find NVIDIA GPU if not specified
+if [[ -z "$PCI_ADDR" ]]; then
+ echo "Scanning for NVIDIA GPUs..."
+ mapfile -t GPUS < <(lspci -D -d 10de: | grep -E "VGA|3D controller" | awk '{print $1}')
+ if [[ ${#GPUS[@]} -eq 0 ]]; then
+ echo "ERROR: No NVIDIA GPU found"
+ exit 1
+ elif [[ ${#GPUS[@]} -eq 1 ]]; then
+ PCI_ADDR="${GPUS[0]}"
+ echo "Auto-selected GPU: $PCI_ADDR"
+ else
+ echo "Multiple GPUs found:"
+ for i in "${!GPUS[@]}"; do
+ echo " [$i] ${GPUS[$i]}"
+ done
+ read -p "Select GPU [0-$((${#GPUS[@]}-1))]: " idx
+ PCI_ADDR="${GPUS[$idx]}"
+ fi
+fi
+
+# Validate PCI address format
+if [[ ! "$PCI_ADDR" =~ ^[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]$ ]]; then
+ echo "ERROR: Invalid PCI address format. Expected: dddd:bb:dd.f"
+ exit 1
+fi
+
+# Check if GPU exists
+if [[ ! -e "/sys/bus/pci/devices/$PCI_ADDR" ]]; then
+ echo "ERROR: PCI device $PCI_ADDR not found"
+ exit 1
+fi
+
+# Validate vGPU type
+if [[ ! " ${VALID_TYPES[*]} " =~ " ${VGPU_TYPE} " ]]; then
+ echo "ERROR: Invalid vGPU type. Valid types: ${VALID_TYPES[*]}"
+ exit 1
+fi
+
+MDEV_TYPE_PATH="/sys/bus/pci/devices/$PCI_ADDR/mdev_supported_types/$VGPU_TYPE"
+
+if [[ ! -d "$MDEV_TYPE_PATH" ]]; then
+ echo "ERROR: vGPU type $VGPU_TYPE not supported on $PCI_ADDR"
+ echo "Available types:"
+ ls "/sys/bus/pci/devices/$PCI_ADDR/mdev_supported_types/" 2>/dev/null || echo " (none - check driver loaded)"
+ exit 1
+fi
+
+# Check available instances
+AVAILABLE=$(cat "$MDEV_TYPE_PATH/available_instances" 2>/dev/null || echo "0")
+if [[ "$AVAILABLE" -le 0 ]]; then
+ echo "ERROR: No available instances for $VGPU_TYPE on $PCI_ADDR"
+ exit 1
+fi
+
+echo "Creating $VGPU_TYPE on $PCI_ADDR..."
+
+# Generate UUID
+UUID=$(uuidgen)
+echo "$UUID" | sudo tee "$MDEV_TYPE_PATH/create" > /dev/null
+
+# Verify creation
+MDEV_PATH="/sys/bus/mdev/devices/$UUID"
+if [[ -d "$MDEV_PATH" ]]; then
+ echo "SUCCESS: Created mdev device $UUID"
+ echo " Path: $MDEV_PATH"
+ echo " PCI: $PCI_ADDR"
+ echo " Type: $VGPU_TYPE"
+ echo ""
+ echo "For QEMU, use:"
+ echo " -device vfio-pci,sysfsdev=$MDEV_PATH,display=off"
+else
+ echo "ERROR: Failed to create mdev device"
+ exit 1
+fi \ No newline at end of file
diff --git a/scripts/launch_qemu_vgpu.sh b/scripts/launch_qemu_vgpu.sh
new file mode 100755
index 0000000..5a57737
--- /dev/null
+++ b/scripts/launch_qemu_vgpu.sh
@@ -0,0 +1,183 @@
+#!/bin/bash
+# launch_qemu_vgpu.sh - Launch QEMU with NVIDIA vGPU mediated device
+# Usage: ./launch_qemu_vgpu.sh [MDEV_UUID] [GUEST_IMAGE] [OPTIONS]
+
+set -euo pipefail
+
+MDEV_UUID="${1:-}"
+GUEST_IMAGE="${2:-/var/lib/libvirt/images/ubuntu-22.04.qcow2}"
+shift 2 || true
+EXTRA_QEMU_ARGS=("$@")
+
+# Find mdev device if UUID not specified
+if [[ -z "$MDEV_UUID" ]]; then
+ echo "Scanning for available mdev devices..."
+ mapfile -t MDEVS < <(find /sys/bus/mdev/devices -maxdepth 1 -name "????????-????-????-????-????????????" -printf "%f\n" 2>/dev/null)
+ if [[ ${#MDEVS[@]} -eq 0 ]]; then
+ echo "ERROR: No mdev devices found. Run create_mdev.sh first."
+ exit 1
+ elif [[ ${#MDEVS[@]} -eq 1 ]]; then
+ MDEV_UUID="${MDEVS[0]}"
+ echo "Auto-selected mdev: $MDEV_UUID"
+ else
+ echo "Multiple mdev devices found:"
+ for i in "${!MDEVS[@]}"; do
+ MDEV="${MDEVS[$i]}"
+ TYPE=$(cat "/sys/bus/mdev/devices/$MDEV/mdev_type/name" 2>/dev/null || echo "unknown")
+ echo " [$i] $MDEV ($TYPE)"
+ done
+ read -p "Select mdev [0-$((${#MDEVS[@]}-1))]: " idx
+ MDEV_UUID="${MDEVS[$idx]}"
+ fi
+fi
+
+MDEV_PATH="/sys/bus/mdev/devices/$MDEV_UUID"
+
+if [[ ! -d "$MDEV_PATH" ]]; then
+ echo "ERROR: mdev device $MDEV_UUID not found at $MDEV_PATH"
+ exit 1
+fi
+
+# Verify guest image
+if [[ ! -f "$GUEST_IMAGE" ]]; then
+ echo "ERROR: Guest image not found: $GUEST_IMAGE"
+ echo "Create one with: qemu-img create -f qcow2 $GUEST_IMAGE 50G"
+ exit 1
+fi
+
+# Check KVM
+if [[ ! -r /dev/kvm ]]; then
+ echo "ERROR: KVM not available (/dev/kvm not readable)"
+ exit 1
+fi
+
+# Check VFIO
+if [[ ! -e /dev/vfio/vfio ]]; then
+ echo "ERROR: VFIO not available"
+ exit 1
+fi
+
+# Get mdev info
+VGPU_TYPE=$(cat "$MDEV_PATH/mdev_type/name" 2>/dev/null || echo "unknown")
+PCI_ADDR=$(readlink -f "$MDEV_PATH/../../.." | xargs basename)
+
+echo "=========================================="
+echo "Launching QEMU with NVIDIA vGPU"
+echo "=========================================="
+echo "mdev UUID: $MDEV_UUID"
+echo "vGPU Type: $VGPU_TYPE"
+echo "PCI Addr: $PCI_ADDR"
+echo "Guest Image: $GUEST_IMAGE"
+echo "Extra Args: ${EXTRA_QEMU_ARGS[*]}"
+echo "=========================================="
+
+# CPU configuration for vGPU
+CPU_OPTS=(
+ -cpu host,hv_time,hv_relaxed,hv_vapic,hv_spinlocks=0x1fff,hv_vendor_id=NVIDIA42
+ -smp 8,sockets=1,cores=8,threads=1
+ -m 16G,slots=4,maxmem=64G
+ -mem-prealloc
+)
+
+# Memory backing for hugepages (better performance)
+HUGEPAGE_OPTS=(
+ -object memory-backend-file,id=mem,size=16G,mem-path=/dev/hugepages,share=on
+ -numa node,memdev=mem
+)
+
+# VFIO device configuration
+VFIO_OPTS=(
+ -device vfio-pci,sysfsdev="$MDEV_PATH",display=off,x-igd-opregion=on
+ -device vfio-pci,sysfsdev="$MDEV_PATH",display=off,romfile=
+)
+
+# Display (use SPICE/VNC for headless, or virtio-gpu for local)
+DISPLAY_OPTS=(
+ -vga none
+ -display none
+ # Uncomment for VNC:
+ # -vnc :1
+ # Uncomment for SPICE:
+ # -spice port=5900,addr=127.0.0.1,disable-ticketing=on
+ # -device virtio-serial-pci
+ # -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0
+ # -chardev spicevmc,id=spicechannel0,name=vdagent
+)
+
+# Network
+NET_OPTS=(
+ -netdev user,id=net0,hostfwd=tcp::2222-:22
+ -device virtio-net-pci,netdev=net0
+)
+
+# Storage
+STORAGE_OPTS=(
+ -drive file="$GUEST_IMAGE",format=qcow2,if=virtio,cache=writeback,aio=threads
+ # Add ISO for installation:
+ # -drive file=/path/to/ubuntu-22.04-live-server-amd64.iso,format=raw,if=virtio,media=cdrom
+)
+
+# USB tablet for better mouse integration
+USB_OPTS=(
+ -usb
+ -device usb-tablet
+)
+
+# RNG
+RNG_OPTS=(
+ -object rng-random,id=rng0,filename=/dev/urandom
+ -device virtio-rng-pci,rng=rng0
+)
+
+# TPM (optional, for Windows guests)
+# TPM_OPTS=(
+# -chardev socket,id=chrtpm,path=/var/run/tpm-emulator/swtpm-sock
+# -tpmdev emulator,id=tpm0,chardev=chrtpm
+# -device tpm-tis,tpmdev=tpm0
+# )
+
+# QEMU command
+QEMU_CMD=(
+ qemu-system-x86_64
+ -enable-kvm
+ -machine q35,accel=kvm,kernel_irqchip=split
+ -bios /usr/share/ovmf/OVMF.fd
+ "${CPU_OPTS[@]}"
+ "${HUGEPAGE_OPTS[@]}"
+ "${VFIO_OPTS[@]}"
+ "${DISPLAY_OPTS[@]}"
+ "${NET_OPTS[@]}"
+ "${STORAGE_OPTS[@]}"
+ "${USB_OPTS[@]}"
+ "${RNG_OPTS[@]}"
+ "${EXTRA_QEMU_ARGS[@]}"
+ -name "vGPU-Guest-$VGPU_TYPE"
+ -pidfile "/tmp/qemu-vgpu-$MDEV_UUID.pid"
+ -daemonize
+)
+
+echo "Starting QEMU..."
+echo "Command: ${QEMU_CMD[*]}"
+echo ""
+
+"${QEMU_CMD[@]}"
+
+sleep 2
+
+if [[ -f "/tmp/qemu-vgpu-$MDEV_UUID.pid" ]]; then
+ PID=$(cat "/tmp/qemu-vgpu-$MDEV_UUID.pid")
+ if kill -0 "$PID" 2>/dev/null; then
+ echo "SUCCESS: QEMU started with PID $PID"
+ echo ""
+ echo "Connect via SSH: ssh -p 2222 user@localhost"
+ echo "Or VNC: vncviewer localhost:1 (if enabled)"
+ echo ""
+ echo "To stop: kill $PID"
+ else
+ echo "ERROR: QEMU failed to start"
+ exit 1
+ fi
+else
+ echo "ERROR: QEMU PID file not created"
+ exit 1
+fi \ No newline at end of file
diff --git a/scripts/load_vgpu_stack.sh b/scripts/load_vgpu_stack.sh
new file mode 100755
index 0000000..36211bc
--- /dev/null
+++ b/scripts/load_vgpu_stack.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+# load_vgpu_stack.sh - Load complete vGPU driver stack
+# Usage: ./load_vgpu_stack.sh [firmware_name]
+
+set -euo pipefail
+
+FIRMWARE_NAME="${1:-gsp-570.144.fwimage.bin}"
+
+BASE_DIR="$OPEN_IOV_ROOT"
+NVIDIA_KO="$BASE_DIR/nvidia-open-gpu-kernel-modules/kernel-open/nvidia.ko"
+VFIO_KO="$BASE_DIR/kernel/open-iov-vfio.ko"
+GSP_VGPU_KO="$BASE_DIR/driver/gsp-vgpu.ko"
+FIRMWARE_PATH="$BASE_DIR/firmware/570-GSP/$FIRMWARE_NAME"
+
+echo "=========================================="
+echo "Loading vGPU Driver Stack"
+echo "=========================================="
+echo "Firmware: $FIRMWARE_NAME"
+echo ""
+
+# Check files exist
+for f in "$NVIDIA_KO" "$VFIO_KO" "$GSP_VGPU_KO" "$FIRMWARE_PATH"; do
+ if [[ ! -f "$f" ]]; then
+ echo "ERROR: Missing file: $f"
+ exit 1
+ fi
+done
+
+# Check if already loaded
+if lsmod | grep -q "^nvidia "; then
+ echo "nvidia.ko already loaded"
+else
+ echo "Loading nvidia.ko (OpenRM)..."
+ sudo insmod "$NVIDIA_KO"
+ echo " OK"
+fi
+
+if lsmod | grep -q "^open_iov_vfio "; then
+ echo "open-iov-vfio.ko already loaded"
+else
+ echo "Loading open-iov-vfio.ko..."
+ sudo insmod "$VFIO_KO"
+ echo " OK"
+fi
+
+if lsmod | grep -q "^gsp_vgpu "; then
+ echo "gsp-vgpu.ko already loaded"
+else
+ echo "Loading gsp-vgpu.ko with firmware=$FIRMWARE_NAME..."
+ sudo insmod "$GSP_VGPU_KO" firmware_name="$FIRMWARE_NAME"
+ echo " OK"
+fi
+
+echo ""
+echo "=========================================="
+echo "Driver Stack Loaded Successfully"
+echo "=========================================="
+lsmod | grep -E "nvidia|open_iov|gsp_vgpu"
+echo ""
+echo "Device nodes:"
+ls -la /dev/gsp-vgpu* /dev/nvidia* 2>/dev/null || echo " (none yet - create mdev first)"
+echo ""
+echo "Next steps:"
+echo " 1. Create mdev: $BASE_DIR/scripts/create_mdev.sh"
+echo " 2. Launch QEMU: $BASE_DIR/scripts/launch_qemu_vgpu.sh" \ No newline at end of file
diff --git a/scripts/setup_guest_driver.sh b/scripts/setup_guest_driver.sh
new file mode 100755
index 0000000..f4f2757
--- /dev/null
+++ b/scripts/setup_guest_driver.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+# setup_guest_driver.sh - Setup NVIDIA driver in QEMU guest
+# Run this INSIDE the guest VM after installation
+
+set -euo pipefail
+
+echo "=========================================="
+echo "Setting up NVIDIA driver in guest"
+echo "=========================================="
+
+# Check if running as root
+if [[ $EUID -ne 0 ]]; then
+ echo "ERROR: This script must be run as root"
+ exit 1
+fi
+
+# Check kernel version
+KERNEL_VER=$(uname -r)
+echo "Kernel: $KERNEL_VER"
+
+# Install dependencies
+echo "Installing build dependencies..."
+apt-get update
+apt-get install -y \
+ build-essential \
+ dkms \
+ linux-headers-"$KERNEL_VER" \
+ linux-headers-generic \
+ pciutils \
+ clinfo
+
+# Check for NVIDIA GPU in guest
+echo "Checking for NVIDIA GPU in guest..."
+lspci -d 10de: | grep -E "VGA|3D controller" || {
+ echo "WARNING: No NVIDIA GPU detected in guest"
+ echo "Make sure QEMU is started with -device vfio-pci,sysfsdev=..."
+}
+
+# Option 1: Install from Ubuntu repository (easiest)
+echo ""
+echo "Choose installation method:"
+echo " 1) Ubuntu repository (nvidia-driver-550-open)"
+echo " 2) NVIDIA runfile (latest)"
+echo " 3) Build from source (open-gpu-kernel-modules)"
+read -p "Select [1-3]: " choice
+
+case $choice in
+ 1)
+ echo "Installing from Ubuntu repository..."
+ apt-get install -y nvidia-driver-550-open
+ ;;
+ 2)
+ echo "Downloading NVIDIA runfile..."
+ RUNFILE="NVIDIA-Linux-x86_64-570.144.run"
+ wget -q "https://us.download.nvidia.com/XFree86/Linux-x86_64/570.144/$RUNFILE"
+ chmod +x "$RUNFILE"
+ ./"$RUNFILE" --silent --dkms --no-opengl-files
+ ;;
+ 3)
+ echo "Building from open-gpu-kernel-modules source..."
+ git clone https://github.com/NVIDIA/open-gpu-kernel-modules.git /tmp/open-gpu-kernel-modules
+ cd /tmp/open-gpu-kernel-modules
+ git checkout 570.144
+ make -C kernel-open/modules SYSSRC=/usr/src/linux-headers-"$KERNEL_VER" -j$(nproc)
+ make -C kernel-open/modules SYSSRC=/usr/src/linux-headers-"$KERNEL_VER" modules_install
+ depmod -a
+ ;;
+ *)
+ echo "Invalid choice"
+ exit 1
+ ;;
+esac
+
+# Install firmware
+echo "Installing GSP firmware..."
+mkdir -p /lib/firmware/nvidia/tu102/gsp
+# Firmware files should be copied from host or downloaded
+# For TU104 (Turing), we need:
+# - bootloader-535.113.01.bin
+# - booter_load-535.113.01.bin
+# - gsp-535.113.01.bin
+echo "Copy firmware files to /lib/firmware/nvidia/tu102/gsp/"
+echo " (bootloader, booter_load, gsp binaries)"
+
+# Load driver
+echo "Loading NVIDIA driver..."
+modprobe nvidia
+
+# Verify
+echo ""
+echo "=========================================="
+echo "Verification"
+echo "=========================================="
+nvidia-smi
+echo ""
+dmesg -T | grep -i nvidia | tail -20
+
+echo ""
+echo "Setup complete! Recommended: reboot the guest" \ No newline at end of file
diff --git a/scripts/test_vgpu_stack.sh b/scripts/test_vgpu_stack.sh
new file mode 100755
index 0000000..d31444d
--- /dev/null
+++ b/scripts/test_vgpu_stack.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+# test_vgpu_stack.sh - Comprehensive vGPU stack test
+
+set -euo pipefail
+
+BASE_DIR="$OPEN_IOV_ROOT"
+LIB_DIR="$BASE_DIR/userspace/lib"
+EXAMPLE="$BASE_DIR/userspace/examples/example_vgpu"
+
+echo "=========================================="
+echo "vGPU Stack Test Suite"
+echo "=========================================="
+
+# Test 1: Module loading
+echo "Test 1: Module loading"
+for mod in nvidia open_iov_vfio gsp_vgpu; do
+ if lsmod | grep -q "^$mod "; then
+ echo " PASS: $mod loaded"
+ else
+ echo " FAIL: $mod not loaded"
+ exit 1
+ fi
+done
+
+# Test 2: Device nodes
+echo "Test 2: Device nodes"
+if [[ -c /dev/gsp-vgpu ]]; then
+ echo " PASS: /dev/gsp-vgpu exists"
+else
+ echo " FAIL: /dev/gsp-vgpu missing"
+ exit 1
+fi
+
+# Test 3: Library loading
+echo "Test 3: Library loading"
+if LD_LIBRARY_PATH="$LIB_DIR" ldd "$EXAMPLE" 2>&1 | grep -q "libiov.so"; then
+ echo " PASS: libiov.so loads correctly"
+else
+ echo " FAIL: libiov.so load failed"
+ exit 1
+fi
+
+# Test 4: Example app help
+echo "Test 4: Example app help"
+if LD_LIBRARY_PATH="$LIB_DIR" "$EXAMPLE" --help 2>&1 | grep -q "Usage"; then
+ echo " PASS: example_vgpu runs"
+else
+ echo " FAIL: example_vgpu failed"
+ exit 1
+fi
+
+# Test 5: mdev creation (if GPU available)
+echo "Test 5: mdev creation"
+GPU_PCI=$(lspci -D -d 10de: | grep -E "VGA|3D controller" | head -1 | awk '{print $1}')
+if [[ -n "$GPU_PCI" ]]; then
+ echo " Found GPU: $GPU_PCI"
+ MDEV_TYPE_PATH="/sys/bus/pci/devices/$GPU_PCI/mdev_supported_types/nvidia-vgpu-turing"
+ if [[ -d "$MDEV_TYPE_PATH" ]]; then
+ AVAIL=$(cat "$MDEV_TYPE_PATH/available_instances" 2>/dev/null || echo 0)
+ if [[ "$AVAIL" -gt 0 ]]; then
+ UUID=$(uuidgen)
+ echo "$UUID" | sudo tee "$MDEV_TYPE_PATH/create" > /dev/null
+ sleep 1
+ if [[ -d "/sys/bus/mdev/devices/$UUID" ]]; then
+ echo " PASS: mdev created: $UUID"
+ # Cleanup
+ echo 1 | sudo tee "/sys/bus/mdev/devices/$UUID/remove" > /dev/null
+ else
+ echo " FAIL: mdev creation failed"
+ exit 1
+ fi
+ else
+ echo " SKIP: No available instances"
+ fi
+ else
+ echo " SKIP: nvidia-vgpu-turing type not available"
+ fi
+else
+ echo " SKIP: No NVIDIA GPU found"
+fi
+
+# Test 6: Kernel log check
+echo "Test 6: Kernel log check"
+if dmesg -T | grep -q "GSP vGPU-IO driver loaded"; then
+ echo " PASS: gsp-vgpu driver loaded successfully"
+else
+ echo " WARN: gsp-vgpu load message not found in dmesg"
+fi
+
+if dmesg -T | grep -q "NVIDIA vGPU VFIO driver loaded"; then
+ echo " PASS: open-iov-vfio driver loaded successfully"
+else
+ echo " WARN: open-iov-vfio load message not found in dmesg"
+fi
+
+# Test 7: Firmware verification
+echo "Test 7: Firmware verification"
+FW="$BASE_DIR/firmware/570-GSP/gsp-570.144.fwimage.bin"
+if [[ -f "$FW" ]]; then
+ SIZE=$(stat -c%s "$FW")
+ EXPECTED=29622272 # ~28.5MB
+ if [[ "$SIZE" -eq "$EXPECTED" ]]; then
+ echo " PASS: Firmware size correct ($SIZE bytes)"
+ else
+ echo " WARN: Firmware size mismatch: $SIZE (expected $EXPECTED)"
+ fi
+
+ # Check ELF header
+ if file "$FW" | grep -q "ELF 64-bit LSB executable, UCB RISC-V"; then
+ echo " PASS: Firmware is valid RISC-V ELF"
+ else
+ echo " FAIL: Firmware is not valid RISC-V ELF"
+ exit 1
+ fi
+else
+ echo " FAIL: Firmware not found"
+ exit 1
+fi
+
+echo ""
+echo "=========================================="
+echo "All Tests Passed!"
+echo "=========================================="
+echo ""
+echo "Stack is ready for QEMU guest launch."
+echo "Run: $BASE_DIR/scripts/create_mdev.sh"
+echo "Then: $BASE_DIR/scripts/launch_qemu_vgpu.sh" \ No newline at end of file
diff --git a/scripts/unload_vgpu_stack.sh b/scripts/unload_vgpu_stack.sh
new file mode 100755
index 0000000..4f57b86
--- /dev/null
+++ b/scripts/unload_vgpu_stack.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+# unload_vgpu_stack.sh - Unload vGPU driver stack
+
+set -euo pipefail
+
+BASE_DIR="$OPEN_IOV_ROOT"
+
+echo "=========================================="
+echo "Unloading vGPU Driver Stack"
+echo "=========================================="
+
+# Remove mdev devices first
+echo "Removing mdev devices..."
+for mdev in /sys/bus/mdev/devices/*/; do
+ if [[ -d "$mdev" ]]; then
+ UUID=$(basename "$mdev")
+ echo " Removing $UUID"
+ echo 1 | sudo tee "$mdev/remove" > /dev/null 2>&1 || true
+ fi
+done
+
+# Unload modules in reverse order
+for mod in gsp_vgpu open_iov_vfio nvidia; do
+ if lsmod | grep -q "^$mod "; then
+ echo "Unloading $mod.ko..."
+ sudo rmmod "$mod" 2>/dev/null || echo " WARNING: $mod still in use"
+ else
+ echo "$mod.ko not loaded"
+ fi
+done
+
+echo ""
+echo "=========================================="
+echo "Driver Stack Unloaded"
+echo "=========================================="
+lsmod | grep -E "nvidia|open_iov|gsp_vgpu" || echo "No vGPU modules loaded" \ No newline at end of file