universalisos/kernel/scripts/build_disk_image.sh
Fábio Coutada 62e8454f91 build: add UEFI ISO builder, test boot scripts, EIM config
- make-uefi-iso.sh: UEFI ISO image builder
- test_boot.S, test_boot.ld: test boot assembly and linker script
- eim_config.toml: EIM (External Interface Module) configuration
- edk2-ovmf RPM for UEFI firmware
- GAP_ANALYSIS_PIKEOS_PARITY.md: PikeOS parity gap analysis
2026-07-15 15:33:07 +01:00

218 lines
6.7 KiB
Bash
Executable file

#!/bin/bash
# UniversalisOS Disk Image Builder
# Creates a bootable GPT disk image with UniversalisOS + guest OS images
# Usage: ./scripts/build_disk_image.sh [--size 32G] [--output universalisos.img]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
KERNEL_DIR="$(dirname "$SCRIPT_DIR")"
GUESTS_DIR="${GUESTS_DIR:-/home/fabiorafaelcoutada/portugalfuturista/guests}"
OUTPUT="${OUTPUT:-${KERNEL_DIR}/universalisos.img}"
SIZE="${SIZE:-32G}"
MNT="/tmp/uos_disk_mount"
ESP_MNT="${MNT}/esp"
DATA_MNT="${MNT}/data"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[UOS]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
err() { echo -e "${RED}[ERR]${NC} $*" >&2; exit 1; }
# Parse args
while [[ $# -gt 0 ]]; do
case $1 in
--size) SIZE="$2"; shift 2;;
--output) OUTPUT="$2"; shift 2;;
--guests) GUESTS_DIR="$2"; shift 2;;
--help) echo "Usage: $0 [--size 32G] [--output universalisos.img] [--guests /path/to/guests]"; exit 0;;
*) err "Unknown option: $1";;
esac
done
# Check dependencies
for cmd in parted mkfs.fat mkfs.ext4 grub2-install mtools; do
command -v "$cmd" &>/dev/null || err "Missing: $cmd (install with: sudo dnf install $cmd)"
done
log "Building UniversalisOS disk image: ${OUTPUT} (${SIZE})"
# ============================================================
# Step 1: Create raw disk image
# ============================================================
log "Creating ${SIZE} disk image..."
dd if=/dev/zero of="$OUTPUT" bs=1 count=0 seek="$SIZE" 2>/dev/null
# ============================================================
# Step 2: Create GPT partition table
# ============================================================
log "Creating GPT partition table..."
parted -s "$OUTPUT" mklabel gpt
parted -s "$OUTPUT" mkpart ESP fat32 1MiB 513MiB
parted -s "$OUTPUT" set 1 esp on
parted -s "$OUTPUT" mkpart primary ext4 513MiB 100%
# ============================================================
# Step 3: Format partitions via loopback
# ============================================================
log "Setting up loopback device..."
LOOP=$(sudo losetup --find --show --partscan "$OUTPUT")
log "Loopback: ${LOOP}"
log "Formatting EFI System Partition (FAT32, 512MB)..."
sudo mkfs.fat -F 32 "${LOOP}p1"
log "Formatting Guest Data Partition (ext4)..."
sudo mkfs.ext4 -F -L uos-guests "${LOOP}p2"
# ============================================================
# Step 4: Mount and populate
# ============================================================
sudo mkdir -p "$ESP_MNT" "$DATA_MNT"
sudo mount "${LOOP}p1" "$ESP_MNT"
sudo mount "${LOOP}p2" "$DATA_MNT"
# --- EFI System Partition ---
log "Installing GRUB2 UEFI bootloader..."
sudo mkdir -p "$ESP_MNT/EFI/BOOT"
sudo mkdir -p "$ESP_MNT/boot/grub"
# Copy kernel
log "Copying kernel..."
sudo cp "${KERNEL_DIR}/build/x86_64/qemu-x86_64-virt/universalisos.elf" "$ESP_MNT/boot/"
# Copy OVMF firmware
log "Copying OVMF firmware..."
sudo cp /usr/share/OVMF/OVMF_CODE.fd "$ESP_MNT/boot/ovmf.fd"
# Create GRUB config
log "Creating GRUB configuration..."
sudo tee "$ESP_MNT/boot/grub/grub.cfg" > /dev/null << 'GRUB_CFG'
set timeout=10
set default=0
# Load GRUB modules
insmod part_gpt
insmod fat
insmod ext2
insmod normal
insmod menuentry
menuentry "UniversalisOS — Android TV STB (IPTV)" {
multiboot /boot/universalisos.elf guest=android-stb
module /boot/ovmf.fd ovmf
boot
}
menuentry "UniversalisOS — Windows 10 Pro" {
multiboot /boot/universalisos.elf guest=windows-10
module /boot/ovmf.fd ovmf
boot
}
menuentry "UniversalisOS — Linux (Debian)" {
multiboot /boot/universalisos.elf guest=linux
module /boot/ovmf.fd ovmf
boot
}
menuentry "UniversalisOS — Safe Mode (no guests)" {
multiboot /boot/universalisos.elf safe
boot
}
menuentry "UniversalisOS — UEFI Shell" {
multiboot /boot/universalisos.elf uefi-shell
boot
}
GRUB_CFG
# Install GRUB EFI binary
log "Installing GRUB EFI binary..."
sudo grub2-install \
--target=x86_64-efi \
--efi-directory="$ESP_MNT" \
--boot-directory="$ESP_MNT/boot" \
--removable \
"$LOOP" 2>/dev/null || warn "grub2-install may need --no-nvram on some systems"
# --- Guest Data Partition ---
log "Copying guest OS images..."
# Copy Windows 10 ISO
if [ -f "$GUESTS_DIR/Windows10Pro.iso" ]; then
log " → Windows 10 Pro ($(du -h "$GUESTS_DIR/Windows10Pro.iso" | cut -f1))"
sudo cp "$GUESTS_DIR/Windows10Pro.iso" "$DATA_MNT/windows10.iso"
fi
# Create guest configuration file
log "Creating guest configuration..."
sudo tee "$DATA_MNT/guests.cfg" > /dev/null << 'GUESTS_CFG'
# UniversalisOS Guest Configuration
# Format: name|type|disk_image|ram_mb|vcpus|firmware
#
# type: windows, linux, android-stb, embedded
# firmware: uefi, bios, baremetal
# Android TV STB — ISP IPTV
android-stb|android-stb|android-stb.img|2048|2|uefi
# Windows 10 Pro
windows-10|windows|windows10.iso|4096|2|uefi
# Linux Debian
linux|linux|linux.img|2048|1|uefi
GUESTS_CFG
# Create placeholder Android STB disk image (1GB sparse)
log "Creating Android STB placeholder image..."
sudo dd if=/dev/zero of="$DATA_MNT/android-stb.img" bs=1 count=0 seek=1G 2>/dev/null
# Create placeholder Linux disk image (2GB sparse)
log "Creating Linux placeholder image..."
sudo dd if=/dev/zero of="$DATA_MNT/linux.img" bs=1 count=0 seek=2G 2>/dev/null
# ============================================================
# Step 5: Install GRUB to MBR (for BIOS fallback)
# ============================================================
log "Installing BIOS fallback GRUB..."
sudo mkdir -p "$ESP_MNT/boot/grub/i386-pc"
sudo grub2-install \
--target=i386-pc \
--boot-directory="$ESP_MNT/boot" \
"$LOOP" 2>/dev/null || warn "BIOS GRUB install skipped (UEFI-only is fine)"
# ============================================================
# Step 6: Cleanup
# ============================================================
log "Unmounting..."
sudo umount "$ESP_MNT"
sudo umount "$DATA_MNT"
sudo losetup -d "$LOOP"
sudo rmdir "$ESP_MNT" "$DATA_MNT" "$MNT" 2>/dev/null || true
# ============================================================
# Step 7: Convert to USB-flashable format
# ============================================================
log "Disk image ready: ${OUTPUT}"
log "Size: $(du -h "$OUTPUT" | cut -f1)"
log ""
log "=== Flash to USB ==="
log " sudo dd if=${OUTPUT} of=/dev/sdX bs=4M status=progress conv=fsync"
log ""
log "=== Flash to NVMe SSD ==="
log " sudo dd if=${OUTPUT} of=/dev/nvme0n1 bs=4M status=progress conv=fsync"
log ""
log "=== Test in QEMU ==="
log " qemu-system-x86_64 \\"
log " -drive file=${OUTPUT},format=raw,if=none,id=disk \\"
log " -device nvme,serial=12345,drive=disk \\"
log " -bios /usr/share/OVMF/OVMF_CODE.fd \\"
log " -m 8192 -smp 2 -serial stdio -display none"
log ""
log "Done!"