From 0ace22d6f88692883584119c64ff010f4cee6738 Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 31 Jul 2026 03:22:20 +0200 Subject: fix(rsdos): organizational changes --- scripts/boot-image.sh | 87 ++++++++++++++++++++++++++++++++++++++ scripts/boot.sh | 100 ++++++++++++++++++++++++++++++++++++++++++++ scripts/build-bootloader.sh | 32 ++++++++++++++ scripts/build-image.sh | 99 +++++++++++++++++++++++++++++++++++++++++++ scripts/debug-kernel.py | 64 ++++++++++++++++++++++++++++ scripts/qemu.sh | 96 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 478 insertions(+) create mode 100755 scripts/boot-image.sh create mode 100755 scripts/boot.sh create mode 100755 scripts/build-bootloader.sh create mode 100755 scripts/build-image.sh create mode 100755 scripts/debug-kernel.py create mode 100755 scripts/qemu.sh (limited to 'scripts') diff --git a/scripts/boot-image.sh b/scripts/boot-image.sh new file mode 100755 index 0000000..913e5cb --- /dev/null +++ b/scripts/boot-image.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# +# scripts/boot-image.sh — boot rs-dos full chain disk image in QEMU +# +# Builds the image first if needed, then boots with UEFI firmware through +# the real boot chain: firmware → rsboot → rsnix kernel. +# +# Usage: +# ./scripts/boot-image.sh [--gui] [--debug] [--mem 512M] [--smp 2] +# +# Keybindings: +# Ctrl-A X — quit +# Ctrl-A C — QEMU monitor +# +# Environment: +# OVMF path to OVMF firmware (default: /usr/share/edk2/x64/OVMF.4m.fd) +# MEM memory size (default: 256M) +# SMP CPU count (default: 1) +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +cd "$PROJECT_DIR" + +IMAGE="rs-dos.img" + +# ---- config ---- +OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" +MEM="${MEM:-256M}" +SMP="${SMP:-1}" +GUI="" +DEBUG="" + +# ---- args ---- +while [ $# -gt 0 ]; do + case "$1" in + --gui | -g) GUI="-vga std" ;; + --debug | -d) DEBUG="-s -S" ;; + --mem) + shift + MEM="$1" + ;; + --smp) + shift + SMP="$1" + ;; + --help | -h) + head -20 "$0" | grep "^#" | sed 's/^# //' + exit 0 + ;; + *) + echo "unknown: $1" + exit 1 + ;; + esac + shift +done + +# ---- build image if missing ---- +if [ ! -f "$IMAGE" ]; then + echo "Image not found, building..." + "$SCRIPT_DIR/build-image.sh" +fi + +# ---- boot ---- +echo "" +echo "=== Booting (full chain: firmware → rsboot → rsnix) ===" +echo " image: $IMAGE" +echo " ovmf: $OVMF" +echo " mem: $MEM smp: $SMP" +echo " gui: $([ -n "$GUI" ] && echo yes || echo no)" +echo " gdb: $([ -n "$DEBUG" ] && echo ':1234' || echo off)" +echo "" + +exec qemu-system-x86_64 \ + -bios "$OVMF" \ + -drive file="$IMAGE",format=raw,if=none,id=hd \ + -device ide-hd,drive=hd \ + -serial stdio \ + -m "$MEM" \ + -machine q35 \ + -cpu qemu64 \ + -no-reboot \ + -smp "$SMP" \ + $GUI $DEBUG diff --git a/scripts/boot.sh b/scripts/boot.sh new file mode 100755 index 0000000..1e3f739 --- /dev/null +++ b/scripts/boot.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# +# scripts/boot.sh — quick direct boot in QEMU (dev loop) +# +# Fastest iteration: boots the kernel directly as BOOTX64.EFI via QEMU's +# FAT driver, skipping the bootloader entirely. +# +# Usage: +# ./scripts/boot.sh [--gui] [--debug] [--release] [--mem 512M] [--smp 2] +# +# Boot chain: firmware → rsnix (direct) +# Full chain: use ./scripts/boot-image.sh (firmware → rsboot → rsnix) +# +# Keybindings: +# Ctrl-A X — quit +# Ctrl-A C — QEMU monitor +# +# Environment: +# OVMF path to OVMF firmware (default: /usr/share/edk2/x64/OVMF.4m.fd) +# MEM memory size (default: 256M) +# SMP CPU count (default: 1) +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +cd "$PROJECT_DIR" + +ESP_DIR="$PROJECT_DIR/esp" + +# ---- config ---- +OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" +MEM="${MEM:-256M}" +SMP="${SMP:-1}" +PROFILE="debug" +CARGO_FLAGS="" +GUI="" +DEBUG="" + +# ---- args ---- +while [ $# -gt 0 ]; do + case "$1" in + --gui | -g) GUI="-vga std" ;; + --debug | -d) DEBUG="-s -S" ;; + --release | -r) + PROFILE="release" + CARGO_FLAGS="--release" + ;; + --mem) + shift + MEM="$1" + ;; + --smp) + shift + SMP="$1" + ;; + --help | -h) + head -20 "$0" | grep "^#" | sed 's/^# //' + exit 0 + ;; + *) + echo "unknown: $1" + exit 1 + ;; + esac + shift +done + +OUT_DIR="target/x86_64-unknown-uefi/$PROFILE" +KERNEL="$OUT_DIR/rsdos.efi" + +# ---- build ---- +echo "=== Building kernel ($PROFILE) ===" +cargo build --package rsdos --target x86_64-unknown-uefi $CARGO_FLAGS + +# ---- deploy ---- +mkdir -p "$ESP_DIR/EFI/BOOT" +cp "$KERNEL" "$ESP_DIR/EFI/BOOT/BOOTX64.EFI" +echo " kernel: $(ls -lh "$KERNEL" | awk '{print $5}')" + +# ---- boot ---- +echo "" +echo "=== Booting (direct: firmware → rsnix) ===" +echo " ovmf: $OVMF" +echo " mem: $MEM smp: $SMP" +echo " gui: $([ -n "$GUI" ] && echo yes || echo no)" +echo " gdb: $([ -n "$DEBUG" ] && echo ':1234' || echo off)" +echo "" + +exec qemu-system-x86_64 \ + -bios "$OVMF" \ + -drive file=fat:rw:"$ESP_DIR",format=raw \ + -serial stdio \ + -m "$MEM" \ + -machine q35 \ + -cpu qemu64 \ + -no-reboot \ + -smp "$SMP" \ + $GUI $DEBUG diff --git a/scripts/build-bootloader.sh b/scripts/build-bootloader.sh new file mode 100755 index 0000000..9a96cfe --- /dev/null +++ b/scripts/build-bootloader.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# +# scripts/build-bootloader.sh — build rsboot (UEFI bootloader) +# +# rsboot is a git submodule at rsboot/. This builds it and places +# BOOTX64.EFI into build/bootloader/. +# +# Usage: +# ./scripts/build-bootloader.sh +# +# Output: +# build/bootloader/BOOTX64.EFI +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +BUILD_DIR="$PROJECT_DIR/build" +BOOTLOADER_DIR="$BUILD_DIR/bootloader" + +echo "=== Building rsboot (UEFI bootloader) ===" + +# Ensure submodule is checked out +git submodule update --init "$PROJECT_DIR/rsboot" + +cd "$PROJECT_DIR/rsboot" +make clean 2>/dev/null || true +make -j"$(nproc)" 2>&1 | sed 's/^/ /' + +echo "" +echo "=== rsboot build complete ===" +ls -lh "$BOOTLOADER_DIR/BOOTX64.EFI" diff --git a/scripts/build-image.sh b/scripts/build-image.sh new file mode 100755 index 0000000..02672ad --- /dev/null +++ b/scripts/build-image.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# +# scripts/build-image.sh — assemble a bootable rs-dos disk image (UEFI/GPT) +# +# Layout: +# Partition 1: ESP (FAT32) — rsboot bootloader + kernel +# +# Output: +# rs-dos.img (or custom path) — bootable raw disk image for UEFI +# +# Usage: +# ./scripts/build-image.sh [output.img] +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +cd "$PROJECT_DIR" + +BUILD_DIR="$PROJECT_DIR/build" +ESP_DIR="$PROJECT_DIR/esp" + +IMAGE="${1:-$PROJECT_DIR/rs-dos.img}" +TARGET=x86_64-unknown-uefi +PROFILE=debug +OUT_DIR="target/${TARGET}/${PROFILE}" + +BOOTLOADER="$BUILD_DIR/bootloader/BOOTX64.EFI" +KERNEL="$OUT_DIR/rsdos.efi" +BZIMAGE="$ESP_DIR/bzImage" +INITRD="$ESP_DIR/initramfs.cpio.gz" +BOOT_EFI="$ESP_DIR/EFI/BOOT/BOOTX64.EFI" + +ESP_START=2048 # 1 MiB offset (standard alignment) +ESP_SIZE_MB=64 + +IMAGE_SIZE_MB=$((ESP_SIZE_MB + 2)) # ESP + GPT gap + backup + +# ---- checks ---- +for cmd in sfdisk mkfs.vfat mcopy truncate dd; do + if ! command -v "$cmd" &>/dev/null; then + echo "Error: $cmd not found" + exit 1 + fi +done + +# ---- build kernel ---- +echo "[1/4] Building kernel..." +cargo build --package rsdos --target x86_64-unknown-uefi ${CARGO_FLAGS:-} + +# ---- build bootloader ---- +echo "[2/4] Building bootloader..." +if [ ! -f "$BOOTLOADER" ]; then + "$SCRIPT_DIR/build-bootloader.sh" +fi + +# ---- assemble ESP ---- +echo "[3/4] Assembling ESP..." +mkdir -p "$ESP_DIR/EFI/BOOT" +cp "$KERNEL" "$BZIMAGE" +cp "$BOOTLOADER" "$BOOT_EFI" +if [ ! -f "$INITRD" ]; then + printf '' | gzip >"$INITRD" +fi + +# ---- create disk image ---- +echo "[4/4] Writing disk image..." +rm -f "$IMAGE" +truncate -s "${IMAGE_SIZE_MB}M" "$IMAGE" + +# GPT partition table: single EFI System partition +sfdisk --quiet "$IMAGE" </dev/null 2>&1 + +# Copy files into FAT +mmd -i "$ESP_IMG" ::/EFI 2>/dev/null || true +mmd -i "$ESP_IMG" ::/EFI/BOOT 2>/dev/null || true +mcopy -i "$ESP_IMG" "$BOOT_EFI" ::/EFI/BOOT/BOOTX64.EFI +mcopy -i "$ESP_IMG" "$BZIMAGE" ::/bzImage 2>/dev/null || true +mcopy -i "$ESP_IMG" "$INITRD" ::/initramfs.cpio.gz 2>/dev/null || true + +# Inject FAT into image at partition offset +dd if="$ESP_IMG" of="$IMAGE" bs=512 seek="$ESP_START" conv=notrunc 2>/dev/null +rm -f "$ESP_IMG" + +echo "" +echo "=== Done: $IMAGE ===" +ls -lh "$IMAGE" +echo "" +echo "Boot with: ./scripts/boot-image.sh" +echo "USB: sudo dd if=$IMAGE of=/dev/sdX bs=4M status=progress" diff --git a/scripts/debug-kernel.py b/scripts/debug-kernel.py new file mode 100755 index 0000000..c794ad6 --- /dev/null +++ b/scripts/debug-kernel.py @@ -0,0 +1,64 @@ +""" +GDB Python script for debugging raisernics under QEMU. + +Usage: + 1. Terminal 1: ./tools/qemu.sh --debug + 2. Terminal 2: gdb -x tools/debug-kernel.py target/x86_64-unknown-uefi/debug/raisernics.efi +""" + +import gdb +import os + +# Find the kernel binary +script_dir = os.path.dirname(os.path.abspath(__file__)) +project_root = os.path.dirname(script_dir) +binary = os.path.join( + project_root, + "target/x86_64-unknown-uefi/debug/raisernics.efi" +) + +# Connect to QEMU's GDB stub +gdb.execute("target remote :1234") + +# Load the binary (add symbol table) +if os.path.exists(binary): + gdb.execute(f"symbol-file {binary}") + print(f"Loaded symbols from {binary}") +else: + print(f"WARNING: {binary} not found. Build with debug symbols first.") + +gdb.execute("set breakpoint pending on") + + +# Helper commands +class DumpBootServices(gdb.Command): + """Dump EFI boot services signature and key pointers""" + + def __init__(self): + super().__init__("dump-bs", gdb.COMMAND_USER) + + def invoke(self, argument, from_tty): + del argument, from_tty + gdb.execute("printf \"SystemTable, BootServices, and key function pointers\\n\"") + gdb.execute("info functions efi_main") + + +class DumpGop(gdb.Command): + """Dump GOP framebuffer info""" + + def __init__(self): + super().__init__("dump-gop", gdb.COMMAND_USER) + + def invoke(self, argument, from_tty): + del argument, from_tty + gdb.execute("printf \"Check the gop::Framebuffer static\\n\"") + + +DumpBootServices() +DumpGop() + +print("") +print("raisernics GDB session ready.") +print(" target remote :1234 (connected)") +print(" Commands: continue, stepi, bt, info registers, etc.") +print(" Custom: dump-bs, dump-gop") diff --git a/scripts/qemu.sh b/scripts/qemu.sh new file mode 100755 index 0000000..b75bed4 --- /dev/null +++ b/scripts/qemu.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# ============================================================================= +# tools/qemu.sh — Run raisernics in QEMU with various options +# +# Usage: +# ./tools/qemu.sh # serial console (no graphics) +# ./tools/qemu.sh --gui # graphical window +# ./tools/qemu.sh --debug # GDB stub on :1234 +# ./tools/qemu.sh --image foo.img # boot from raw disk image +# ./tools/qemu.sh --bios /path/to.fd # custom OVMF +# ./tools/qemu.sh --mem 512M # memory size +# ./tools/qemu.sh --smp 2 # SMP cores +# ============================================================================= +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$SCRIPT_DIR" + +# Defaults +GUI_FLAG="" +DEBUG_FLAG="" +DRIVE_ARGS=(-drive file=fat:rw:esp,format=raw) +OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" +MEM="${MEM:-256M}" +SMP="${SMP:-1}" + +# Pre-build +make all + +# Parse args +while [ $# -gt 0 ]; do + case "$1" in + --gui | -g) + GUI_FLAG="-vga std" + shift + ;; + --debug | -d) + DEBUG_FLAG="-s -S" + shift + ;; + --image | -i) + _img_file="$(printf -- '-drive file=%s,format=raw,if=none,id=hd' "$2")" + DRIVE_ARGS=("$_img_file" '-device' 'ide-hd,drive=hd') + shift 2 + ;; + --bios | -b) + OVMF="$2" + shift 2 + ;; + --mem) + MEM="$2" + shift 2 + ;; + --smp) + SMP="$2" + shift 2 + ;; + --help | -h) + head -20 "$0" | grep "^# " | sed 's/^# //' + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +QEMU_CMD=( + qemu-system-x86_64 + -bios "$OVMF" + "${DRIVE_ARGS[@]}" + -serial stdio + -m "$MEM" + -machine q35 + -cpu qemu64 + -no-reboot + -smp "$SMP" +) + +[ -n "$GUI_FLAG" ] && QEMU_CMD+=("$GUI_FLAG") +if [ -n "$DEBUG_FLAG" ]; then + # Split the flag string safely into words + read -ra dbg <<<"$DEBUG_FLAG" + QEMU_CMD+=("${dbg[@]}") +fi + +echo " OVMF : $OVMF" +echo " Drive : ${DRIVE_ARGS[*]}" +echo " Mem : $MEM" +echo " SMP : $SMP" +echo " GDB : $([ -n "$DEBUG_FLAG" ] && echo ':1234' || echo 'off')" +echo " GUI : $([ -n "$GUI_FLAG" ] && echo 'yes' || echo 'no')" +echo "" + +exec "${QEMU_CMD[@]}" -- cgit v1.2.3