#!/bin/bash set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Function to print colored messages print_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } print_info() { echo -e "[INFO] $1"; } # Function to find OVMF firmware files on Arch Linux find_ovmf_firmware() { local firmware_type="$1" # CODE or VARS # Arch Linux OVMF paths (from edk2-ovmf package) local ovmf_paths=( "/usr/share/edk2/x64/OVMF_${firmware_type}.4m.fd" "/usr/share/edk2-ovmf/x64/OVMF_${firmware_type}.fd" "/usr/share/ovmf/x64/OVMF_${firmware_type}.4m.fd" ) for path in "${ovmf_paths[@]}"; do if [ -f "$path" ]; then echo "$path" return 0 fi done return 1 } # Function to check and install Arch Linux dependencies check_requirements() { local errors=0 local packages_to_install=() # Check for QEMU if ! pacman -Qi qemu-full &>/dev/null && ! pacman -Qi qemu-system-x86 &>/dev/null; then print_warning "QEMU is not installed" packages_to_install+=("qemu-full") else print_success "QEMU is installed" fi # Check for OVMF firmware if ! pacman -Qi edk2-ovmf &>/dev/null; then print_warning "OVMF UEFI firmware is not installed" packages_to_install+=("edk2-ovmf") else print_success "OVMF firmware is installed" fi # Install missing packages if [ ${#packages_to_install[@]} -gt 0 ]; then print_info "Installing missing packages: ${packages_to_install[*]}" sudo pacman -S --noconfirm --needed "${packages_to_install[@]}" if [ $? -ne 0 ]; then print_error "Failed to install required packages" ((errors++)) fi fi # Check for KVM support if [ ! -e /dev/kvm ]; then print_warning "KVM is not available. VM will run without hardware acceleration (slower)" print_info "To enable KVM:" print_info " 1. Ensure virtualization is enabled in BIOS" print_info " 2. Load KVM modules: sudo modprobe kvm kvm_intel (or kvm_amd)" KVM_AVAILABLE=false else if [ ! -r /dev/kvm ] || [ ! -w /dev/kvm ]; then print_warning "KVM permissions issue. Add user to kvm group: sudo usermod -a -G kvm $USER" KVM_AVAILABLE=false else KVM_AVAILABLE=true print_success "KVM hardware acceleration is available" fi fi # Find OVMF firmware files OVMF_CODE=$(find_ovmf_firmware "CODE") if [ -z "$OVMF_CODE" ]; then print_error "Could not find OVMF CODE firmware" print_info "Install with: sudo pacman -S edk2-ovmf" ((errors++)) else print_success "Found OVMF CODE at: $OVMF_CODE" fi OVMF_VARS=$(find_ovmf_firmware "VARS") if [ -z "$OVMF_VARS" ]; then # On Arch, VARS might not exist separately, use CODE OVMF_VARS="$OVMF_CODE" print_info "Using OVMF CODE for VARS" else print_success "Found OVMF VARS at: $OVMF_VARS" fi return $errors } # Function to setup storage setup_storage() { local storage_dir="${STORAGE_DIR:-/tmp}" if [ ! -d "$storage_dir" ]; then print_error "Storage directory does not exist: $storage_dir" exit 1 fi if [ ! -f "$storage_dir/test.qcow2" ]; then print_info "Creating virtual disk at $storage_dir/test.qcow2..." qemu-img create -f qcow2 "$storage_dir/test.qcow2" 20G else print_info "Using existing disk at $storage_dir/test.qcow2" fi if [ ! -f "$storage_dir/test_large.qcow2" ]; then print_info "Creating second virtual disk at $storage_dir/test_large.qcow2..." qemu-img create -f qcow2 "$storage_dir/test_large.qcow2" 20G else print_info "Using existing disk at $storage_dir/test_large.qcow2" fi DISK1="$storage_dir/test.qcow2" DISK2="$storage_dir/test_large.qcow2" } # Function to build QEMU command build_qemu_command() { local iso_file="$1" local qemu_args=() # Basic configuration qemu_args+=("qemu-system-x86_64") # CPU and acceleration if [ "$KVM_AVAILABLE" = true ]; then qemu_args+=("-cpu" "host") qemu_args+=("-enable-kvm") qemu_args+=("-machine" "q35,accel=kvm") else qemu_args+=("-cpu" "qemu64") qemu_args+=("-machine" "q35") fi # Memory local total_mem=$(free -m | awk '/^Mem:/{print $2}') local vm_mem="${MEMORY:-8192}" if [ "$total_mem" -lt 10240 ] && [ "$vm_mem" -gt 4096 ]; then vm_mem=4096 print_warning "Limited system memory. Using ${vm_mem}MB for VM" fi qemu_args+=("-m" "$vm_mem") # UEFI firmware (unless BIOS mode requested) if [ -n "$OVMF_CODE" ] && [ "$BOOT_MODE" != "bios" ]; then # Persist firmaware as well if $STORAGE_DIR is specified if [ -z "$STORAGE_DIR" ]; then # Create writable VARS file from the correct template OVMF_VARS_COPY="/tmp/OVMF_VARS_$$.fd" if [ -f "$OVMF_VARS" ]; then cp "$OVMF_VARS" "$OVMF_VARS_COPY" else # Fallback: create empty VARS file if template not found dd if=/dev/zero of="$OVMF_VARS_COPY" bs=1M count=4 2>/dev/null fi else OVMF_VARS_COPY="$STORAGE_DIR/OVMF_VARS.fd" # Only create firmaware file if it doesn't exist to allow persistence if [ ! -f "$OVMF_VARS_COPY" ]; then if [ -f "$OVMF_VARS" ]; then cp "$OVMF_VARS" "$OVMF_VARS_COPY" else # Fallback: create empty VARS file if template not found dd if=/dev/zero of="$OVMF_VARS_COPY" bs=1M count=4 2>/dev/null fi fi fi qemu_args+=("-drive" "if=pflash,format=raw,readonly=on,file=$OVMF_CODE") qemu_args+=("-drive" "if=pflash,format=raw,file=$OVMF_VARS_COPY") fi # Storage devices - Smart boot order that handles both installation and post-install # Credits to Ryan Hughes for this elegant solution: # - Empty disk (bootindex=1) fails to boot -> falls back to ISO (bootindex=2) # - After installation, disk is bootable -> boots from disk automatically # This achieves David's goal without any complex monitoring! qemu_args+=("-drive" "file=$DISK1,format=qcow2,if=none,id=drive0") qemu_args+=("-device" "virtio-blk-pci,drive=drive0,bootindex=1") qemu_args+=("-drive" "file=$DISK2,format=qcow2,if=none,id=drive1") qemu_args+=("-device" "virtio-blk-pci,drive=drive1,bootindex=3") # CD-ROM with ISO - set as second boot device (fallback) qemu_args+=("-drive" "file=$iso_file,media=cdrom,if=none,format=raw,id=cdrom0") qemu_args+=("-device" "ide-cd,drive=cdrom0,bootindex=2") qemu_args+=("-device" "virtio-vga-gl") qemu_args+=("-display" "gtk,gl=on") # Input devices qemu_args+=("-usb" "-device" "usb-tablet") # Network qemu_args+=("-netdev" "user,id=net0") qemu_args+=("-device" "virtio-net-pci,netdev=net0") echo "${qemu_args[@]}" } # Function to show usage show_usage() { echo "Omarchy ISO Boot Tool - Arch Linux" echo "" echo "Usage: $0 [options]" echo "" echo "Options:" echo " --bios Force BIOS boot mode (default: UEFI)" echo " --storage DIR Directory for VM storage (default: /tmp)" echo " --memory MB Memory allocation in MB (default: 8192)" echo " --no-kvm Disable KVM acceleration" echo " --help Show this help message" echo "" echo "Examples:" echo " $0 omarchy.iso" echo " $0 omarchy.iso --memory 4096" echo " $0 omarchy.iso --bios --storage ~/vms" echo "" } # Main function main() { print_info "Omarchy ISO Boot Tool for Arch Linux" echo "======================================" # Parse arguments if [ $# -eq 0 ] || [ "$1" = "--help" ] || [ "$1" = "-h" ]; then show_usage exit 0 fi ISO_FILE="$1" shift # Check if ISO exists if [ ! -f "$ISO_FILE" ]; then print_error "ISO file not found: $ISO_FILE" exit 1 fi print_success "ISO file: $ISO_FILE ($(du -h "$ISO_FILE" | cut -f1))" # Parse additional options while [ $# -gt 0 ]; do case "$1" in --bios) BOOT_MODE="bios" print_info "Using BIOS boot mode" ;; --storage) STORAGE_DIR="$2" shift ;; --memory) MEMORY="$2" shift ;; --no-kvm) KVM_AVAILABLE=false print_info "KVM acceleration disabled by user" ;; *) print_warning "Unknown option: $1" ;; esac shift done # Check system requirements print_info "Checking system requirements..." if ! check_requirements; then print_error "System requirements not met" exit 1 fi # Setup storage print_info "Setting up storage..." setup_storage print_info "Boot order: Disk (if bootable) -> ISO (fallback)" print_info "After installation, system will automatically boot from disk" # Build QEMU command QEMU_CMD=$(build_qemu_command "$ISO_FILE") # Show command for debugging print_info "QEMU command:" echo " $QEMU_CMD" | fold -s -w 80 | sed '2,$s/^/ /' echo "" print_success "Starting VM... (Press Ctrl-A X to exit)" echo "======================================" # Execute QEMU eval $QEMU_CMD # Cleanup if [ -f "$OVMF_VARS_COPY" ]; then rm -f "$OVMF_VARS_COPY" fi } # Cleanup on exit cleanup() { if [ -f "$OVMF_VARS_COPY" ]; then rm -f "$OVMF_VARS_COPY" fi } trap cleanup EXIT # Run main function main "$@"