Volt VMM (Neutron Stardust): source-available under AGPSL v5.0
KVM-based microVMM for the Volt platform: - Sub-second VM boot times - Minimal memory footprint - Landlock LSM + seccomp security - Virtio device support - Custom kernel management Copyright (c) Armored Gates LLC. All rights reserved. Licensed under AGPSL v5.0
This commit is contained in:
262
scripts/build-kernel.sh
Executable file
262
scripts/build-kernel.sh
Executable file
@@ -0,0 +1,262 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# build-kernel.sh - Build an optimized microVM kernel for Volt
|
||||
#
|
||||
# This script downloads and builds a minimal Linux kernel configured
|
||||
# specifically for fast-booting microVMs with KVM virtualization.
|
||||
#
|
||||
# Requirements:
|
||||
# - gcc, make, flex, bison, libelf-dev, libssl-dev
|
||||
# - ~2GB disk space, ~10 min build time
|
||||
#
|
||||
# Output: kernels/vmlinux (uncompressed kernel for direct boot)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
BUILD_DIR="${PROJECT_DIR}/.build/kernel"
|
||||
OUTPUT_DIR="${PROJECT_DIR}/kernels"
|
||||
|
||||
# Kernel version - LTS for stability
|
||||
KERNEL_VERSION="${KERNEL_VERSION:-6.6.51}"
|
||||
KERNEL_MAJOR="${KERNEL_VERSION%%.*}"
|
||||
KERNEL_URL="https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_MAJOR}.x/linux-${KERNEL_VERSION}.tar.xz"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log() { echo -e "${GREEN}[+]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
error() { echo -e "${RED}[✗]${NC} $*"; exit 1; }
|
||||
|
||||
check_dependencies() {
|
||||
log "Checking build dependencies..."
|
||||
local deps=(gcc make flex bison bc perl)
|
||||
local missing=()
|
||||
|
||||
for dep in "${deps[@]}"; do
|
||||
if ! command -v "$dep" &>/dev/null; then
|
||||
missing+=("$dep")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
error "Missing dependencies: ${missing[*]}"
|
||||
fi
|
||||
|
||||
# Check for headers
|
||||
if [[ ! -f /usr/include/libelf.h ]] && [[ ! -f /usr/include/elfutils/libelf.h ]]; then
|
||||
warn "libelf-dev might be missing (needed for BTF)"
|
||||
fi
|
||||
}
|
||||
|
||||
download_kernel() {
|
||||
log "Downloading Linux kernel ${KERNEL_VERSION}..."
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
if [[ -d "linux-${KERNEL_VERSION}" ]]; then
|
||||
log "Kernel source already exists, skipping download"
|
||||
return
|
||||
fi
|
||||
|
||||
local tarball="linux-${KERNEL_VERSION}.tar.xz"
|
||||
if [[ ! -f "$tarball" ]]; then
|
||||
curl -L -o "$tarball" "$KERNEL_URL"
|
||||
fi
|
||||
|
||||
log "Extracting kernel source..."
|
||||
tar xf "$tarball"
|
||||
}
|
||||
|
||||
create_config() {
|
||||
log "Creating minimal microVM kernel config..."
|
||||
|
||||
cd "${BUILD_DIR}/linux-${KERNEL_VERSION}"
|
||||
|
||||
# Start with a minimal config
|
||||
make allnoconfig
|
||||
|
||||
# Apply microVM-specific options
|
||||
cat >> .config << 'EOF'
|
||||
# Basic system
|
||||
CONFIG_64BIT=y
|
||||
CONFIG_SMP=y
|
||||
CONFIG_NR_CPUS=128
|
||||
CONFIG_PREEMPT_VOLUNTARY=y
|
||||
CONFIG_HIGH_RES_TIMERS=y
|
||||
CONFIG_NO_HZ_IDLE=y
|
||||
CONFIG_HZ_100=y
|
||||
|
||||
# PVH boot support (direct kernel boot)
|
||||
CONFIG_PVH=y
|
||||
CONFIG_XEN_PVH=y
|
||||
|
||||
# KVM guest support
|
||||
CONFIG_HYPERVISOR_GUEST=y
|
||||
CONFIG_PARAVIRT=y
|
||||
CONFIG_KVM_GUEST=y
|
||||
CONFIG_PARAVIRT_CLOCK=y
|
||||
CONFIG_PARAVIRT_SPINLOCKS=y
|
||||
|
||||
# Memory
|
||||
CONFIG_MEMORY_HOTPLUG=y
|
||||
CONFIG_MEMORY_BALLOON=y
|
||||
CONFIG_VIRTIO_BALLOON=y
|
||||
CONFIG_BALLOON_COMPACTION=y
|
||||
|
||||
# Block devices
|
||||
CONFIG_BLOCK=y
|
||||
CONFIG_BLK_DEV=y
|
||||
CONFIG_VIRTIO_BLK=y
|
||||
|
||||
# Networking
|
||||
CONFIG_NET=y
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_INET=y
|
||||
CONFIG_VIRTIO_NET=y
|
||||
CONFIG_VHOST_NET=y
|
||||
|
||||
# VirtIO core
|
||||
CONFIG_VIRTIO=y
|
||||
CONFIG_VIRTIO_MMIO=y
|
||||
CONFIG_VIRTIO_PCI=y
|
||||
CONFIG_VIRTIO_PCI_LEGACY=n
|
||||
CONFIG_VIRTIO_CONSOLE=y
|
||||
|
||||
# Filesystems
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_PROC_FS=y
|
||||
CONFIG_SYSFS=y
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
CONFIG_TMPFS=y
|
||||
CONFIG_SQUASHFS=y
|
||||
CONFIG_SQUASHFS_ZSTD=y
|
||||
|
||||
# TTY/Serial (for console)
|
||||
CONFIG_TTY=y
|
||||
CONFIG_VT=n
|
||||
CONFIG_SERIAL_8250=y
|
||||
CONFIG_SERIAL_8250_CONSOLE=y
|
||||
CONFIG_SERIAL_8250_NR_UARTS=4
|
||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
|
||||
|
||||
# Minimal character devices
|
||||
CONFIG_UNIX98_PTYS=y
|
||||
CONFIG_DEVMEM=y
|
||||
|
||||
# Init
|
||||
CONFIG_BINFMT_ELF=y
|
||||
CONFIG_BINFMT_SCRIPT=y
|
||||
|
||||
# Crypto (minimal for boot)
|
||||
CONFIG_CRYPTO=y
|
||||
CONFIG_CRYPTO_CRC32C_INTEL=y
|
||||
|
||||
# Disable unnecessary features
|
||||
CONFIG_MODULES=n
|
||||
CONFIG_PRINTK=y
|
||||
CONFIG_BUG=y
|
||||
CONFIG_DEBUG_INFO=n
|
||||
CONFIG_KALLSYMS=n
|
||||
CONFIG_FTRACE=n
|
||||
CONFIG_PROFILING=n
|
||||
CONFIG_DEBUG_KERNEL=n
|
||||
|
||||
# 9P for host filesystem sharing
|
||||
CONFIG_NET_9P=y
|
||||
CONFIG_NET_9P_VIRTIO=y
|
||||
CONFIG_9P_FS=y
|
||||
|
||||
# Compression support for initrd
|
||||
CONFIG_RD_GZIP=y
|
||||
CONFIG_RD_ZSTD=y
|
||||
|
||||
# Disable legacy/unused
|
||||
CONFIG_USB_SUPPORT=n
|
||||
CONFIG_SOUND=n
|
||||
CONFIG_INPUT=n
|
||||
CONFIG_SERIO=n
|
||||
CONFIG_HW_RANDOM=y
|
||||
CONFIG_HW_RANDOM_VIRTIO=y
|
||||
CONFIG_DRM=n
|
||||
CONFIG_FB=n
|
||||
CONFIG_AGP=n
|
||||
CONFIG_ACPI=n
|
||||
CONFIG_PNP=n
|
||||
CONFIG_WIRELESS=n
|
||||
CONFIG_WLAN=n
|
||||
CONFIG_RFKILL=n
|
||||
CONFIG_BLUETOOTH=n
|
||||
CONFIG_I2C=n
|
||||
CONFIG_SPI=n
|
||||
CONFIG_HWMON=n
|
||||
CONFIG_THERMAL=n
|
||||
CONFIG_WATCHDOG=n
|
||||
CONFIG_MD=n
|
||||
CONFIG_BT=n
|
||||
CONFIG_NFS_FS=n
|
||||
CONFIG_CIFS=n
|
||||
CONFIG_SECURITY=n
|
||||
CONFIG_AUDIT=n
|
||||
EOF
|
||||
|
||||
# Resolve any conflicts
|
||||
make olddefconfig
|
||||
}
|
||||
|
||||
build_kernel() {
|
||||
log "Building kernel (this may take 5-15 minutes)..."
|
||||
|
||||
cd "${BUILD_DIR}/linux-${KERNEL_VERSION}"
|
||||
|
||||
# Parallel build using all cores
|
||||
local jobs
|
||||
jobs=$(nproc)
|
||||
|
||||
make -j"$jobs" vmlinux
|
||||
|
||||
# Copy output
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
cp vmlinux "${OUTPUT_DIR}/vmlinux"
|
||||
|
||||
# Create a symlink to the versioned kernel
|
||||
ln -sf vmlinux "${OUTPUT_DIR}/vmlinux-${KERNEL_VERSION}"
|
||||
}
|
||||
|
||||
show_stats() {
|
||||
local kernel="${OUTPUT_DIR}/vmlinux"
|
||||
|
||||
if [[ -f "$kernel" ]]; then
|
||||
log "Kernel built successfully!"
|
||||
echo ""
|
||||
echo " Path: $kernel"
|
||||
echo " Size: $(du -h "$kernel" | cut -f1)"
|
||||
echo " Kernel version: ${KERNEL_VERSION}"
|
||||
echo ""
|
||||
echo "To use with Volt:"
|
||||
echo " volt-vmm --kernel ${kernel} --rootfs <rootfs> ..."
|
||||
else
|
||||
error "Kernel build failed - vmlinux not found"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main
|
||||
main() {
|
||||
log "Building Volt microVM kernel v${KERNEL_VERSION}"
|
||||
echo ""
|
||||
|
||||
check_dependencies
|
||||
download_kernel
|
||||
create_config
|
||||
build_kernel
|
||||
show_stats
|
||||
}
|
||||
|
||||
main "$@"
|
||||
291
scripts/build-rootfs.sh
Executable file
291
scripts/build-rootfs.sh
Executable file
@@ -0,0 +1,291 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# build-rootfs.sh - Create a minimal Alpine rootfs for Volt testing
|
||||
#
|
||||
# This script creates a small, fast-booting root filesystem suitable
|
||||
# for microVM testing. Uses Alpine Linux for its minimal footprint.
|
||||
#
|
||||
# Requirements:
|
||||
# - curl, tar
|
||||
# - e2fsprogs (mkfs.ext4) or squashfs-tools (mksquashfs)
|
||||
# - Optional: sudo (for proper permissions)
|
||||
#
|
||||
# Output: images/alpine-rootfs.ext4 (or .squashfs)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
BUILD_DIR="${PROJECT_DIR}/.build/rootfs"
|
||||
OUTPUT_DIR="${PROJECT_DIR}/images"
|
||||
|
||||
# Alpine version
|
||||
ALPINE_VERSION="${ALPINE_VERSION:-3.19}"
|
||||
ALPINE_RELEASE="${ALPINE_RELEASE:-3.19.1}"
|
||||
ALPINE_ARCH="x86_64"
|
||||
ALPINE_URL="https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/releases/${ALPINE_ARCH}/alpine-minirootfs-${ALPINE_RELEASE}-${ALPINE_ARCH}.tar.gz"
|
||||
|
||||
# Image settings
|
||||
IMAGE_FORMAT="${IMAGE_FORMAT:-ext4}" # ext4 or squashfs
|
||||
IMAGE_SIZE_MB="${IMAGE_SIZE_MB:-64}" # Size for ext4 images
|
||||
IMAGE_NAME="alpine-rootfs"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[+]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
error() { echo -e "${RED}[✗]${NC} $*"; exit 1; }
|
||||
|
||||
check_dependencies() {
|
||||
log "Checking dependencies..."
|
||||
|
||||
local deps=(curl tar)
|
||||
|
||||
case "$IMAGE_FORMAT" in
|
||||
ext4) deps+=(mkfs.ext4) ;;
|
||||
squashfs) deps+=(mksquashfs) ;;
|
||||
*) error "Unknown format: $IMAGE_FORMAT" ;;
|
||||
esac
|
||||
|
||||
for dep in "${deps[@]}"; do
|
||||
if ! command -v "$dep" &>/dev/null; then
|
||||
error "Missing dependency: $dep"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
download_alpine() {
|
||||
log "Downloading Alpine minirootfs ${ALPINE_RELEASE}..."
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
local tarball="${BUILD_DIR}/alpine-minirootfs.tar.gz"
|
||||
if [[ ! -f "$tarball" ]]; then
|
||||
curl -L -o "$tarball" "$ALPINE_URL"
|
||||
else
|
||||
log "Using cached download"
|
||||
fi
|
||||
}
|
||||
|
||||
extract_rootfs() {
|
||||
log "Extracting rootfs..."
|
||||
|
||||
local rootfs="${BUILD_DIR}/rootfs"
|
||||
rm -rf "$rootfs"
|
||||
mkdir -p "$rootfs"
|
||||
|
||||
# Extract (needs root for proper permissions, but works without)
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
tar xzf "${BUILD_DIR}/alpine-minirootfs.tar.gz" -C "$rootfs"
|
||||
else
|
||||
# Fakeroot alternative or just extract
|
||||
tar xzf "${BUILD_DIR}/alpine-minirootfs.tar.gz" -C "$rootfs" 2>/dev/null || \
|
||||
tar xzf "${BUILD_DIR}/alpine-minirootfs.tar.gz" -C "$rootfs" --no-same-owner
|
||||
warn "Extracted without root - some permissions may be incorrect"
|
||||
fi
|
||||
}
|
||||
|
||||
customize_rootfs() {
|
||||
log "Customizing rootfs for microVM boot..."
|
||||
|
||||
local rootfs="${BUILD_DIR}/rootfs"
|
||||
|
||||
# Create init script for fast boot
|
||||
cat > "${rootfs}/init" << 'INIT'
|
||||
#!/bin/sh
|
||||
# Volt microVM init
|
||||
|
||||
# Mount essential filesystems
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sys /sys
|
||||
mount -t devtmpfs dev /dev
|
||||
|
||||
# Set hostname
|
||||
hostname volt-vmm-vm
|
||||
|
||||
# Print boot message
|
||||
echo ""
|
||||
echo "======================================"
|
||||
echo " Volt microVM booted!"
|
||||
echo " Alpine Linux $(cat /etc/alpine-release)"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
# Show boot time if available
|
||||
if [ -f /proc/uptime ]; then
|
||||
uptime=$(cut -d' ' -f1 /proc/uptime)
|
||||
echo "Boot time: ${uptime}s"
|
||||
fi
|
||||
|
||||
# Start shell
|
||||
exec /bin/sh
|
||||
INIT
|
||||
chmod +x "${rootfs}/init"
|
||||
|
||||
# Create minimal inittab
|
||||
cat > "${rootfs}/etc/inittab" << 'EOF'
|
||||
::sysinit:/etc/init.d/rcS
|
||||
::respawn:-/bin/sh
|
||||
ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100
|
||||
::shutdown:/bin/umount -a -r
|
||||
EOF
|
||||
|
||||
# Configure serial console
|
||||
mkdir -p "${rootfs}/etc/init.d"
|
||||
cat > "${rootfs}/etc/init.d/rcS" << 'EOF'
|
||||
#!/bin/sh
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sys /sys
|
||||
mount -t devtmpfs dev /dev
|
||||
hostname volt-vmm-vm
|
||||
EOF
|
||||
chmod +x "${rootfs}/etc/init.d/rcS"
|
||||
|
||||
# Set up basic networking config
|
||||
mkdir -p "${rootfs}/etc/network"
|
||||
cat > "${rootfs}/etc/network/interfaces" << 'EOF'
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
auto eth0
|
||||
iface eth0 inet dhcp
|
||||
EOF
|
||||
|
||||
# Disable unnecessary services
|
||||
rm -f "${rootfs}/etc/init.d/hwclock"
|
||||
rm -f "${rootfs}/etc/init.d/hwdrivers"
|
||||
|
||||
# Create fstab
|
||||
cat > "${rootfs}/etc/fstab" << 'EOF'
|
||||
/dev/vda / ext4 defaults,noatime 0 1
|
||||
proc /proc proc defaults 0 0
|
||||
sys /sys sysfs defaults 0 0
|
||||
devpts /dev/pts devpts defaults 0 0
|
||||
EOF
|
||||
|
||||
log "Rootfs customized for fast boot"
|
||||
}
|
||||
|
||||
create_ext4_image() {
|
||||
log "Creating ext4 image (${IMAGE_SIZE_MB}MB)..."
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
local image="${OUTPUT_DIR}/${IMAGE_NAME}.ext4"
|
||||
local rootfs="${BUILD_DIR}/rootfs"
|
||||
|
||||
# Create sparse file
|
||||
dd if=/dev/zero of="$image" bs=1M count=0 seek="$IMAGE_SIZE_MB" 2>/dev/null
|
||||
|
||||
# Format
|
||||
mkfs.ext4 -F -L rootfs -O ^metadata_csum "$image" >/dev/null
|
||||
|
||||
# Mount and copy (requires root)
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
local mnt="${BUILD_DIR}/mnt"
|
||||
mkdir -p "$mnt"
|
||||
mount -o loop "$image" "$mnt"
|
||||
cp -a "${rootfs}/." "$mnt/"
|
||||
umount "$mnt"
|
||||
else
|
||||
# Use debugfs to copy files (limited but works without root)
|
||||
warn "Creating image without root - using alternative method"
|
||||
|
||||
# Create a tar and extract into image using e2tools or fuse
|
||||
if command -v e2cp &>/dev/null; then
|
||||
# Use e2tools
|
||||
find "$rootfs" -type f | while read -r file; do
|
||||
local dest="${file#$rootfs}"
|
||||
e2cp "$file" "$image:$dest" 2>/dev/null || true
|
||||
done
|
||||
else
|
||||
warn "e2fsprogs-extra not available - image will be empty"
|
||||
warn "Install e2fsprogs-extra or run as root for full rootfs"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$image"
|
||||
}
|
||||
|
||||
create_squashfs_image() {
|
||||
log "Creating squashfs image..."
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
local image="${OUTPUT_DIR}/${IMAGE_NAME}.squashfs"
|
||||
local rootfs="${BUILD_DIR}/rootfs"
|
||||
|
||||
mksquashfs "$rootfs" "$image" \
|
||||
-comp zstd \
|
||||
-Xcompression-level 19 \
|
||||
-noappend \
|
||||
-quiet
|
||||
|
||||
echo "$image"
|
||||
}
|
||||
|
||||
create_image() {
|
||||
local image
|
||||
|
||||
case "$IMAGE_FORMAT" in
|
||||
ext4) image=$(create_ext4_image) ;;
|
||||
squashfs) image=$(create_squashfs_image) ;;
|
||||
esac
|
||||
|
||||
echo "$image"
|
||||
}
|
||||
|
||||
show_stats() {
|
||||
local image="$1"
|
||||
|
||||
log "Rootfs image created successfully!"
|
||||
echo ""
|
||||
echo " Path: $image"
|
||||
echo " Size: $(du -h "$image" | cut -f1)"
|
||||
echo " Format: $IMAGE_FORMAT"
|
||||
echo " Base: Alpine Linux ${ALPINE_RELEASE}"
|
||||
echo ""
|
||||
echo "To use with Volt:"
|
||||
echo " volt-vmm --kernel kernels/vmlinux --rootfs $image"
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--format)
|
||||
IMAGE_FORMAT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--size)
|
||||
IMAGE_SIZE_MB="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help)
|
||||
echo "Usage: $0 [--format ext4|squashfs] [--size MB]"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
error "Unknown option: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Main
|
||||
main() {
|
||||
log "Building Volt test rootfs"
|
||||
echo ""
|
||||
|
||||
check_dependencies
|
||||
download_alpine
|
||||
extract_rootfs
|
||||
customize_rootfs
|
||||
|
||||
local image
|
||||
image=$(create_image)
|
||||
|
||||
show_stats "$image"
|
||||
}
|
||||
|
||||
main
|
||||
234
scripts/run-vm.sh
Executable file
234
scripts/run-vm.sh
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# run-vm.sh - Launch a test VM with Volt
|
||||
#
|
||||
# This script provides sensible defaults for testing Volt.
|
||||
# It checks for required assets and provides helpful error messages.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/run-vm.sh # Run with defaults
|
||||
# ./scripts/run-vm.sh --memory 256 # Custom memory
|
||||
# ./scripts/run-vm.sh --kernel <path> # Custom kernel
|
||||
# ./scripts/run-vm.sh --rootfs <path> # Custom rootfs
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Default paths
|
||||
KERNEL="${KERNEL:-${PROJECT_DIR}/kernels/vmlinux}"
|
||||
ROOTFS="${ROOTFS:-${PROJECT_DIR}/images/alpine-rootfs.ext4}"
|
||||
|
||||
# VM configuration defaults
|
||||
MEMORY="${MEMORY:-128}" # MB
|
||||
CPUS="${CPUS:-1}"
|
||||
VM_NAME="${VM_NAME:-volt-vmm-test}"
|
||||
API_SOCKET="${API_SOCKET:-/tmp/volt-vmm-${VM_NAME}.sock}"
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL="${LOG_LEVEL:-info}"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[+]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
error() { echo -e "${RED}[✗]${NC} $*"; exit 1; }
|
||||
info() { echo -e "${CYAN}[i]${NC} $*"; }
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
Launch a test VM with Volt.
|
||||
|
||||
Options:
|
||||
--kernel PATH Path to kernel (default: kernels/vmlinux)
|
||||
--rootfs PATH Path to rootfs image (default: images/alpine-rootfs.ext4)
|
||||
--memory MB Memory in MB (default: 128)
|
||||
--cpus N Number of vCPUs (default: 1)
|
||||
--name NAME VM name (default: volt-vmm-test)
|
||||
--debug Enable debug logging
|
||||
--dry-run Show command without executing
|
||||
--help Show this help
|
||||
|
||||
Environment variables:
|
||||
KERNEL, ROOTFS, MEMORY, CPUS, VM_NAME, LOG_LEVEL
|
||||
|
||||
Examples:
|
||||
$0 # Run with defaults
|
||||
$0 --memory 256 --cpus 2 # Custom resources
|
||||
$0 --debug # Verbose logging
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
DRY_RUN=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--kernel)
|
||||
KERNEL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--rootfs)
|
||||
ROOTFS="$2"
|
||||
shift 2
|
||||
;;
|
||||
--memory)
|
||||
MEMORY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--cpus)
|
||||
CPUS="$2"
|
||||
shift 2
|
||||
;;
|
||||
--name)
|
||||
VM_NAME="$2"
|
||||
API_SOCKET="/tmp/volt-vmm-${VM_NAME}.sock"
|
||||
shift 2
|
||||
;;
|
||||
--debug)
|
||||
LOG_LEVEL="debug"
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
error "Unknown option: $1 (use --help for usage)"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
check_kvm() {
|
||||
if [[ ! -e /dev/kvm ]]; then
|
||||
error "KVM not available (/dev/kvm not found)
|
||||
|
||||
Make sure:
|
||||
1. Your CPU supports virtualization (VT-x/AMD-V)
|
||||
2. Virtualization is enabled in BIOS
|
||||
3. KVM modules are loaded (modprobe kvm kvm_intel or kvm_amd)"
|
||||
fi
|
||||
|
||||
if [[ ! -r /dev/kvm ]] || [[ ! -w /dev/kvm ]]; then
|
||||
error "Cannot access /dev/kvm
|
||||
|
||||
Fix with: sudo usermod -aG kvm \$USER && newgrp kvm"
|
||||
fi
|
||||
|
||||
log "KVM available"
|
||||
}
|
||||
|
||||
check_assets() {
|
||||
# Check kernel
|
||||
if [[ ! -f "$KERNEL" ]]; then
|
||||
error "Kernel not found: $KERNEL
|
||||
|
||||
Build it with: just build-kernel
|
||||
Or specify with: --kernel <path>"
|
||||
fi
|
||||
log "Kernel: $KERNEL"
|
||||
|
||||
# Check rootfs
|
||||
if [[ ! -f "$ROOTFS" ]]; then
|
||||
# Try squashfs if ext4 not found
|
||||
local alt_rootfs="${ROOTFS%.ext4}.squashfs"
|
||||
if [[ -f "$alt_rootfs" ]]; then
|
||||
ROOTFS="$alt_rootfs"
|
||||
else
|
||||
error "Rootfs not found: $ROOTFS
|
||||
|
||||
Build it with: just build-rootfs
|
||||
Or specify with: --rootfs <path>"
|
||||
fi
|
||||
fi
|
||||
log "Rootfs: $ROOTFS"
|
||||
}
|
||||
|
||||
check_binary() {
|
||||
local binary="${PROJECT_DIR}/target/release/volt-vmm"
|
||||
|
||||
if [[ ! -x "$binary" ]]; then
|
||||
binary="${PROJECT_DIR}/target/debug/volt-vmm"
|
||||
fi
|
||||
|
||||
if [[ ! -x "$binary" ]]; then
|
||||
error "Volt binary not found
|
||||
|
||||
Build it with: just build (or just release)"
|
||||
fi
|
||||
|
||||
echo "$binary"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
# Remove stale socket
|
||||
rm -f "$API_SOCKET"
|
||||
}
|
||||
|
||||
run_vm() {
|
||||
local binary
|
||||
binary=$(check_binary)
|
||||
|
||||
# Build command
|
||||
local cmd=(
|
||||
"$binary"
|
||||
--kernel "$KERNEL"
|
||||
--rootfs "$ROOTFS"
|
||||
--memory "$MEMORY"
|
||||
--cpus "$CPUS"
|
||||
--api-socket "$API_SOCKET"
|
||||
)
|
||||
|
||||
# Add kernel command line for console
|
||||
cmd+=(--cmdline "console=ttyS0 reboot=k panic=1 nomodules")
|
||||
|
||||
echo ""
|
||||
info "VM Configuration:"
|
||||
echo " Name: $VM_NAME"
|
||||
echo " Memory: ${MEMORY}MB"
|
||||
echo " CPUs: $CPUS"
|
||||
echo " Kernel: $KERNEL"
|
||||
echo " Rootfs: $ROOTFS"
|
||||
echo " Socket: $API_SOCKET"
|
||||
echo ""
|
||||
|
||||
if $DRY_RUN; then
|
||||
info "Dry run - would execute:"
|
||||
echo " RUST_LOG=$LOG_LEVEL ${cmd[*]}"
|
||||
return
|
||||
fi
|
||||
|
||||
info "Starting VM (Ctrl+C to exit)..."
|
||||
echo ""
|
||||
|
||||
# Cleanup on exit
|
||||
trap cleanup EXIT
|
||||
|
||||
# Run!
|
||||
RUST_LOG="$LOG_LEVEL" exec "${cmd[@]}"
|
||||
}
|
||||
|
||||
# Main
|
||||
main() {
|
||||
echo ""
|
||||
log "Volt Test VM Launcher"
|
||||
echo ""
|
||||
|
||||
check_kvm
|
||||
check_assets
|
||||
run_vm
|
||||
}
|
||||
|
||||
main
|
||||
Reference in New Issue
Block a user