From 76103215711d268b4b02f62e900f2be3032cf295 Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 31 Jul 2026 02:18:30 +0200 Subject: Initial commit: raisernics toy kernel - Bare-metal x86_64 UEFI kernel (PE32+, no libc) - GOP framebuffer + 8x16 bitmap font - PS/2 keyboard (polling, scancode set 1) - UART 16550 serial driver (COM1, 115200 8N1) - Bump allocator as #[global_allocator] (alloc::String, Vec, format!) - UEFI memory map capture (static buffer, ADR 0001) - Shell with 7 commands (help, echo, clear, hello, info, mem, halt) - Dual output: GOP framebuffer + serial console - Makefile: run, run-gui, debug, release, image targets - tools/: qemu.sh, boot-image.sh, debug-kernel.py --- tools/boot-image.sh | 62 +++++++++++++++++++++++++++++++++ tools/debug-kernel.py | 64 ++++++++++++++++++++++++++++++++++ tools/qemu.sh | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100755 tools/boot-image.sh create mode 100755 tools/debug-kernel.py create mode 100755 tools/qemu.sh (limited to 'tools') diff --git a/tools/boot-image.sh b/tools/boot-image.sh new file mode 100755 index 0000000..2592afa --- /dev/null +++ b/tools/boot-image.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# ============================================================================= +# tools/boot-image.sh — Create a bootable disk image from the ESP directory +# +# Produces a raw disk image (bootable via UEFI) for dd-ing to a USB stick +# or attaching to QEMU as a hard disk. +# +# Usage: +# ./tools/boot-image.sh [output.img] +# +# Default output: raisernics.img +# ============================================================================= +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$SCRIPT_DIR" + +OUTPUT="${1:-raisernics.img}" +ESP_DIR="${ESP_DIR:-esp}" +MBR_SZ=1 # 1 MB reserved at start for GPT/pmbr +SECTOR_SZ=512 + +# Rebuild ESP if stale +make all + +if [ ! -d "$ESP_DIR" ]; then + echo "ERROR: ESP directory '$ESP_DIR' not found. Run 'make all' first." + exit 1 +fi + +# Calculate required size: ESP contents + MBR gap + some slack +ESP_SIZE_MB=$(du -sm "$ESP_DIR" 2>/dev/null | cut -f1 || echo 1) +TOTAL_MB=$((ESP_SIZE_MB + MBR_SZ + 1)) # +1 MB slack + +echo "Creating ${TOTAL_MB} MB disk image: ${OUTPUT}" + +# Create empty image with MBR gap +dd if=/dev/zero of="$OUTPUT" bs=1M count="$TOTAL_MB" status=none 2>/dev/null + +# Partition table: single GPT partition, type EFI System (C12A7328-F81F-11D2-BA4B-00A0C93EC93B) +sgdisk --clear \ + --new=1:${MBR_SZ}M:0 \ + --typecode=1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B \ + --change-name=1:"raisernics" \ + "$OUTPUT" >/dev/null + +# Find partition offset +PART_START=$(sgdisk --info=1 "$OUTPUT" 2>/dev/null | grep "Partition start sector" | awk '{print $NF}') +if [ -z "$PART_START" ]; then + PART_START=$((MBR_SZ * 1024 * 1024 / SECTOR_SZ)) +fi + +# Format as FAT32 +mformat -i "$OUTPUT"@$((PART_START * SECTOR_SZ)) -h 32 -s 32 -t "$((TOTAL_MB * 2))" >/dev/null 2>&1 || true + +# Copy the ESP contents onto the partition +mcopy -i "$OUTPUT"@$((PART_START * SECTOR_SZ)) -s "$ESP_DIR"/* ::/ 2>/dev/null + +echo " -> ${OUTPUT} ($(ls -lh "$OUTPUT" | awk '{print $5}'))" +echo "" +echo "To write to USB: sudo dd if=${OUTPUT} of=/dev/sdX bs=4M status=progress" +echo "To test with QEMU: qemu-system-x86_64 -bios -drive file=${OUTPUT},format=raw" diff --git a/tools/debug-kernel.py b/tools/debug-kernel.py new file mode 100755 index 0000000..c794ad6 --- /dev/null +++ b/tools/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/tools/qemu.sh b/tools/qemu.sh new file mode 100755 index 0000000..428954f --- /dev/null +++ b/tools/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