From 298528581d1a29032a1224600297316384d4e4f7 Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 21 Aug 2026 19:13:52 +0200 Subject: image: single dd-able system.img replaces vvfat+data-disk pair MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mksystem.sh stages one FAT32 image: \bzImage + \initramfs.cpio.gz (rsbl paths), \EFI\BOOT\BOOTX64.EFI (kernel direct by default, --chain for rsboot), \etc\motd.txt, empty \bin\cli + \bin\gui, seed files at the root. --gpt wraps it in a partition table for real USB dd targets. Makefile: run/run-gui/debug attach only system.img on ide.0 (vvfat lines deleted); stamp-based 'system' target rebuilds only when the kernel is newer, so runtime writes survive reboots; system-fresh wipes; inspect lists via mtools. build-image.sh/mkdisk.sh removed. test-disk.sh boots system.img with a throwaway OVMF varstore — a stale NvVars makes the firmware skip the \EFI\BOOT fallback. sendkeys.py pacing 50ms->250ms/1.5s: keystroke loss under host load corrupted typed lines (guest-side PS/2 ring overflow). Gate: test-disk PASS (fsck clean, file survives poweroff). --- scripts/build-image.sh | 99 -------------------------- scripts/mkdisk.sh | 91 ------------------------ scripts/mksystem.sh | 184 +++++++++++++++++++++++++++++++++++++++++++++++++ scripts/sendkeys.py | 4 +- scripts/test-disk.sh | 20 +++--- 5 files changed, 196 insertions(+), 202 deletions(-) delete mode 100755 scripts/build-image.sh delete mode 100755 scripts/mkdisk.sh create mode 100755 scripts/mksystem.sh (limited to 'scripts') diff --git a/scripts/build-image.sh b/scripts/build-image.sh deleted file mode 100755 index 02672ad..0000000 --- a/scripts/build-image.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/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/mkdisk.sh b/scripts/mkdisk.sh deleted file mode 100755 index 2c62f76..0000000 --- a/scripts/mkdisk.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env bash -# -# scripts/mkdisk.sh — create (or re-create) the FAT32 data disk that the -# kernel mounts at /disk. -# -# Usage: -# ./scripts/mkdisk.sh # create /tmp/disk.img (64M) if missing -# ./scripts/mkdisk.sh --fresh # re-format (wipes the image!) -# ./scripts/mkdisk.sh --size 128M # different size (default 64M) -# ./scripts/mkdisk.sh /path/img # custom image path -# ./scripts/mkdisk.sh --no-seed # skip the seed/ directory -# ./scripts/mkdisk.sh --label CRASHLOG # volume label (default RSDOS) -# -# The image is created with `mkfs.fat -C`, which is the option that -# actually creates the file — plain `mkfs.fat` only writes into an -# existing one. Files from seed/ (if present) are copied in with mtools. -# -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" - -IMG="/tmp/disk.img" -SIZE="64M" -FRESH=0 -SEED_DIR="$PROJECT_DIR/seed" -LABEL="RSDOS" - -while [ $# -gt 0 ]; do - case "$1" in - --fresh) FRESH=1 ;; - --size) - shift - SIZE="$1" - ;; - --no-seed) SEED_DIR="" ;; - --label) - shift - LABEL="$1" - ;; - --help | -h) - head -20 "$0" | grep "^#" | sed 's/^# \?//' - exit 0 - ;; - *) - IMG="$1" - ;; - esac - shift -done - -if [ -f "$IMG" ] && [ "$FRESH" -eq 0 ]; then - echo "mkdisk: $IMG already exists (use --fresh to re-format)" - exit 0 -fi - -if ! command -v mkfs.fat >/dev/null 2>&1; then - echo "mkdisk: mkfs.fat not found — install dosfstools (sudo apt install dosfstools)" >&2 - exit 1 -fi - -# mkfs.fat sizes are 1024-byte blocks; accept M/G/K suffixes too. -case "$SIZE" in -*G) SIZE_BLOCKS=$((${SIZE%G} * 1024 * 1024)) ;; -*M) SIZE_BLOCKS=$((${SIZE%M} * 1024)) ;; -*K) SIZE_BLOCKS=$((${SIZE%K})) ;; -*) SIZE_BLOCKS="$SIZE" ;; -esac -if [ -z "$SIZE_BLOCKS" ] || [ "$SIZE_BLOCKS" -le 0 ] 2>/dev/null; then - echo "mkdisk: bad size '$SIZE' (use e.g. 64M or 65536)" >&2 - exit 1 -fi - -echo "mkdisk: creating $IMG ($SIZE FAT32, label $LABEL)" -rm -f "$IMG" -mkfs.fat -C -F 32 -n "$LABEL" "$IMG" "$SIZE_BLOCKS" - -if [ -n "$SEED_DIR" ] && [ -d "$SEED_DIR" ]; then - if ! command -v mcopy >/dev/null 2>&1; then - echo "mkdisk: warning: mcopy not found — skipping seed files (install mtools)" >&2 - else - echo "mkdisk: seeding from $SEED_DIR/" - for f in "$SEED_DIR"/*; do - [ -f "$f" ] || continue - mcopy -i "$IMG" "$f" "::/$(basename "$f")" - echo " -> $(basename "$f")" - done - fi -fi - -echo "mkdisk: done — boot with: make run (or: ./scripts/boot.sh)" diff --git a/scripts/mksystem.sh b/scripts/mksystem.sh new file mode 100755 index 0000000..dfa481c --- /dev/null +++ b/scripts/mksystem.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +# +# scripts/mksystem.sh — assemble the single dd-able rs-dos system image +# +# Replaces the old two-source setup (vvfat `esp/` directory for boot + +# separate /tmp/disk.img for data). One FAT32 image holds EVERYTHING: +# +# \bzImage kernel (rsbl requires this exact path) +# \initramfs.cpio.gz empty initramfs (rsbl requires it) +# \EFI\BOOT\BOOTX64.EFI boot entry: the kernel (default) or rsboot +# \etc\motd.txt seed config +# \bin\cli\ \bin\gui\ binary tree (empty until Phase 2/4 migrate tools) +# \*.txt seed files (seed/) +# +# The firmware boots from this image and the kernel mounts the same +# volume at /disk// — boot medium and data medium are one disk. +# +# Usage: +# ./scripts/mksystem.sh [options] [output.img] # default /tmp/system.img +# +# Options: +# --force rebuild even if the image exists (Makefile stamps handle +# normal freshness; without --force an existing image is kept, +# so user files survive across reboots) +# --size NM image size in MB (default 64) +# --chain \EFI\BOOT\BOOTX64.EFI = rsboot bootloader (full chain: +# firmware → rsboot → rsnix). Default is the kernel itself +# (direct: firmware → rsnix), matching the old dev loop. +# --gpt wrap the FAT volume in a GPT partition table (for dd-ing +# to real USB sticks whose firmware refuses superfloppies) +# --label NAME FAT volume label (default RSDOS) +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +cd "$PROJECT_DIR" + +IMG="/tmp/system.img" +SIZE_MB=64 +FORCE=0 +CHAIN=0 +GPT=0 +LABEL="RSDOS" + +while [ $# -gt 0 ]; do + case "$1" in + --force | --fresh) + FORCE=1 + ;; + --size) + shift + SIZE_MB="$1" + ;; + --chain) + CHAIN=1 + ;; + --gpt) + GPT=1 + ;; + --label) + shift + LABEL="$1" + ;; + --help | -h) + head -30 "$0" | grep "^#" | sed 's/^# \?//' + exit 0 + ;; + -*) + echo "mksystem: unknown option: $1" >&2 + exit 1 + ;; + *) + IMG="$1" + ;; + esac + shift +done + +TARGET=x86_64-unknown-uefi +PROFILE=debug +OUT_DIR="target/$TARGET/$PROFILE" +KERNEL="$OUT_DIR/rsdos.efi" +BZIMAGE="esp/bzImage" +INITRD="esp/initramfs.cpio.gz" +BOOTLOADER="build/bootloader/BOOTX64.EFI" + +# ---- checks ---- +for cmd in mkfs.fat mcopy mmd; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo "mksystem: $cmd not found (install dosfstools + mtools)" >&2 + exit 1 + fi +done +if [ "$GPT" -eq 1 ]; then + for cmd in sfdisk truncate dd; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo "mksystem: --gpt needs $cmd" >&2 + exit 1 + fi + done +fi + +if [ -f "$IMG" ] && [ "$FORCE" -eq 0 ]; then + echo "mksystem: $IMG already exists (use --force to re-create)" + exit 0 +fi + +# ---- build kernel ---- +if [ ! -f "$KERNEL" ]; then + echo "mksystem: building kernel..." + cargo build --package rsdos --target "$TARGET" +fi + +# ---- build bootloader (chain mode only) ---- +if [ "$CHAIN" -eq 1 ] && [ ! -f "$BOOTLOADER" ]; then + echo "mksystem: building bootloader..." + "$SCRIPT_DIR/build-bootloader.sh" +fi + +# ---- deploy kernel artifacts to esp/ (shared with the old layout) ---- +mkdir -p esp/EFI/BOOT +cp "$KERNEL" "$BZIMAGE" +[ -f "$INITRD" ] || printf '' | gzip >"$INITRD" + +# ---- create the FAT volume ---- +rm -f "$IMG" +SIZE_BLOCKS=$((SIZE_MB * 1024)) # mkfs.fat counts 1024-byte blocks +echo "mksystem: creating $IMG (${SIZE_MB}M FAT32, label $LABEL)" +mkfs.fat -C -F 32 -n "$LABEL" "$IMG" "$SIZE_BLOCKS" >/dev/null + +echo "mksystem: staging system tree" +mmd -i "$IMG" ::/EFI ::/EFI/BOOT ::/etc ::/bin ::/bin/cli ::/bin/gui +mcopy -i "$IMG" "$BZIMAGE" ::/bzImage +mcopy -i "$IMG" "$INITRD" ::/initramfs.cpio.gz +if [ "$CHAIN" -eq 1 ]; then + mcopy -i "$IMG" "$BOOTLOADER" ::/EFI/BOOT/BOOTX64.EFI + echo " \\EFI\\BOOT\\BOOTX64.EFI <- rsboot (full chain)" +else + mcopy -i "$IMG" "$KERNEL" ::/EFI/BOOT/BOOTX64.EFI + echo " \\EFI\\BOOT\\BOOTX64.EFI <- kernel (direct)" +fi +echo " \\bzImage \\initramfs.cpio.gz \\bin{\\cli,\\gui} \\etc" + +# Seed files: motd lives in \etc, everything else at the volume root. +SEED_DIR="$PROJECT_DIR/seed" +if [ -d "$SEED_DIR" ]; then + for f in "$SEED_DIR"/*; do + [ -f "$f" ] || continue + name="$(basename "$f")" + if [ "$name" = "motd.txt" ]; then + mcopy -i "$IMG" "$f" ::/etc/"$name" + echo " \\etc\\$name" + else + mcopy -i "$IMG" "$f" ::/"$name" + echo " \\$name" + fi + done +fi + +# ---- optional GPT wrapper (real-hardware dd target) ---- +if [ "$GPT" -eq 1 ]; then + GPT_START=2048 # 1 MiB offset (standard alignment) + GPT_IMG="$(mktemp)" + truncate -s "${SIZE_MB}M" "$GPT_IMG" + dd if="$IMG" of="$GPT_IMG" bs=512 seek="$GPT_START" conv=notrunc 2>/dev/null + FINAL="$(mktemp)" + truncate -s "$((SIZE_MB + 2))M" "$FINAL" + sfdisk --quiet "$FINAL" </dev/null + rm -f "$GPT_IMG" + mv "$FINAL" "$IMG" + echo "mksystem: wrapped in GPT (partition at LBA $GPT_START)" +fi + +echo "" +echo "=== Done: $IMG ===" +ls -lh "$IMG" +echo "Boot with: make run (or: make boot-image)" +[ "$GPT" -eq 0 ] && echo "USB dd tip: add --gpt for firmwares that refuse superfloppies" +exit 0 diff --git a/scripts/sendkeys.py b/scripts/sendkeys.py index 1c97b33..a67903d 100755 --- a/scripts/sendkeys.py +++ b/scripts/sendkeys.py @@ -72,9 +72,9 @@ def type_line(sock, line): else: sys.stderr.write("warning: no sendkey mapping for %r\n" % ch) continue - time.sleep(0.05) + time.sleep(0.25) sendkey(sock, "ret") - time.sleep(0.7) + time.sleep(1.5) def main(): diff --git a/scripts/test-disk.sh b/scripts/test-disk.sh index 0268fff..9ffb3c8 100755 --- a/scripts/test-disk.sh +++ b/scripts/test-disk.sh @@ -26,7 +26,7 @@ FRESH=0 OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" QEMU="${QEMU:-qemu-system-x86_64}" -IMG="${DISK_IMG:-/tmp/disk.img}" +IMG="${SYSTEM_IMG:-/tmp/system.img}" ESP_DIR="$PROJECT_DIR/esp" QPID=/tmp/rsdos-test-qpid SOCK=/tmp/rsdos-test-qmon.sock @@ -69,29 +69,29 @@ wait_qemu_exit() { echo "==> 1/6 build kernel (debug)" cargo build --package rsdos --target x86_64-unknown-uefi -echo "==> 2/6 deploy to ESP + ensure disk image" -mkdir -p "$ESP_DIR/EFI/BOOT" -cp target/x86_64-unknown-uefi/debug/rsdos.efi "$ESP_DIR/EFI/BOOT/BOOTX64.EFI" +echo "==> 2/6 ensure system image" if [ "$FRESH" -eq 1 ]; then - "$SCRIPT_DIR/mkdisk.sh" --fresh "$IMG" + "$SCRIPT_DIR/mksystem.sh" --force "$IMG" else - "$SCRIPT_DIR/mkdisk.sh" "$IMG" + "$SCRIPT_DIR/mksystem.sh" "$IMG" fi echo "==> 3/6 boot QEMU (headless)" kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true sleep 1 rm -f "$SOCK" "$LOG" -"$QEMU" -bios "$OVMF" \ +# Throwaway OVMF varstore: a stale/poisoned NvVars (e.g. left by the old +# vvfat setup) makes the firmware skip the \EFI\BOOT fallback entirely. +NV="/tmp/rsdos-test-ovmf-vars.fd" +rm -f "$NV" +cp /usr/share/edk2/x64/OVMF.4m.fd "$NV" +"$QEMU" -bios "$NV" \ -drive file="$IMG",format=raw,if=none,id=disk \ -device ide-hd,drive=disk,bus=ide.0 \ - -drive file=fat:rw:"$ESP_DIR",format=raw,if=none,id=esp \ - -device ide-hd,drive=esp,bus=ide.1 \ -serial file:"$LOG" \ -monitor unix:"$SOCK",server,nowait \ -m 256M -machine q35 -cpu qemu64 -no-reboot -display none \ >"$QLOG" 2>&1 & -echo $! >"$QPID" wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" sleep 1 -- cgit v1.2.3