diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-21 19:13:52 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-21 19:13:52 +0200 |
| commit | 298528581d1a29032a1224600297316384d4e4f7 (patch) | |
| tree | f9a7a320567f5cd88cd3f322fcab0be16418be2c | |
| parent | docs: ADR 0006 (executable binaries) + implementation plan + glossary terms (diff) | |
| download | rs-dos-298528581d1a29032a1224600297316384d4e4f7.tar.gz rs-dos-298528581d1a29032a1224600297316384d4e4f7.zip | |
image: single dd-able system.img replaces vvfat+data-disk pair
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).
| -rw-r--r-- | Makefile | 66 | ||||
| -rwxr-xr-x | scripts/build-image.sh | 99 | ||||
| -rwxr-xr-x | scripts/mkdisk.sh | 91 | ||||
| -rwxr-xr-x | scripts/mksystem.sh | 184 | ||||
| -rwxr-xr-x | scripts/sendkeys.py | 4 | ||||
| -rwxr-xr-x | scripts/test-disk.sh | 20 |
6 files changed, 226 insertions, 238 deletions
| @@ -10,15 +10,14 @@ export INITRD=$(ESP_DIR)/initramfs.cpio.gz | |||
| 10 | export BOOTLOADER=rsboot/rsboot.efi | 10 | export BOOTLOADER=rsboot/rsboot.efi |
| 11 | export OVMF=/usr/share/edk2/x64/OVMF.4m.fd | 11 | export OVMF=/usr/share/edk2/x64/OVMF.4m.fd |
| 12 | export BOOT_EFI=$(ESP_DIR)/EFI/BOOT/BOOTX64.EFI | 12 | export BOOT_EFI=$(ESP_DIR)/EFI/BOOT/BOOTX64.EFI |
| 13 | # Attach a raw disk image as the SATA drive (disk.img -> ide.0, ESP -> ide.1). | 13 | # Attach the single dd-able system image as the SATA drive (ide.0). |
| 14 | # Create one with: make disk (or: ./scripts/mkdisk.sh) | 14 | # Create/re-create with: make system-fresh (or: ./scripts/mksystem.sh --force) |
| 15 | export DISK_IMG ?= /tmp/disk.img | 15 | export SYSTEM_IMG ?= /tmp/system.img |
| 16 | 16 | ||
| 17 | .PHONY: all run run-gui clean image debug release fmt bootloader boot-image disk disk-fresh inspect test-disk test-mouse test-gfx test-fault test-msg | ||
| 18 | 17 | ||
| 19 | # ---- Build --------------------------------------------------------------- | 18 | # ---- Build --------------------------------------------------------------- |
| 20 | 19 | ||
| 21 | all: $(BZIMAGE) $(INITRD) | 20 | .PHONY: all run run-gui clean image debug release fmt bootloader boot-image system system-fresh inspect test-disk test-mouse test-gfx test-fault test-msg |
| 22 | 21 | ||
| 23 | release: PROFILE=release | 22 | release: PROFILE=release |
| 24 | release: export PROFILE=release | 23 | release: export PROFILE=release |
| @@ -43,13 +42,11 @@ $(INITRD): | $(ESP_DIR) | |||
| 43 | 42 | ||
| 44 | # ---- QEMU runners -------------------------------------------------------- | 43 | # ---- QEMU runners -------------------------------------------------------- |
| 45 | 44 | ||
| 46 | run: all disk | 45 | run: all system |
| 47 | qemu-system-x86_64 \ | 46 | qemu-system-x86_64 \ |
| 48 | -bios $(OVMF) \ | 47 | -bios $(OVMF) \ |
| 49 | -drive file=$(DISK_IMG),format=raw,if=none,id=disk \ | 48 | -drive file=$(SYSTEM_IMG),format=raw,if=none,id=disk \ |
| 50 | -device ide-hd,drive=disk,bus=ide.0 \ | 49 | -device ide-hd,drive=disk,bus=ide.0 \ |
| 51 | -drive file=fat:rw:$(ESP_DIR),format=raw,if=none,id=esp \ | ||
| 52 | -device ide-hd,drive=esp,bus=ide.1 \ | ||
| 53 | -serial stdio \ | 50 | -serial stdio \ |
| 54 | -m 256M \ | 51 | -m 256M \ |
| 55 | -machine q35 \ | 52 | -machine q35 \ |
| @@ -57,13 +54,11 @@ run: all disk | |||
| 57 | -no-reboot \ | 54 | -no-reboot \ |
| 58 | -display none | 55 | -display none |
| 59 | 56 | ||
| 60 | run-gui: all disk | 57 | run-gui: all system |
| 61 | qemu-system-x86_64 \ | 58 | qemu-system-x86_64 \ |
| 62 | -bios $(OVMF) \ | 59 | -bios $(OVMF) \ |
| 63 | -drive file=$(DISK_IMG),format=raw,if=none,id=disk \ | 60 | -drive file=$(SYSTEM_IMG),format=raw,if=none,id=disk \ |
| 64 | -device ide-hd,drive=disk,bus=ide.0 \ | 61 | -device ide-hd,drive=disk,bus=ide.0 \ |
| 65 | -drive file=fat:rw:$(ESP_DIR),format=raw,if=none,id=esp \ | ||
| 66 | -device ide-hd,drive=esp,bus=ide.1 \ | ||
| 67 | -serial stdio \ | 62 | -serial stdio \ |
| 68 | -m 256M \ | 63 | -m 256M \ |
| 69 | -machine q35 \ | 64 | -machine q35 \ |
| @@ -72,13 +67,11 @@ run-gui: all disk | |||
| 72 | -vga std | 67 | -vga std |
| 73 | 68 | ||
| 74 | # GDB stub on :1234 | 69 | # GDB stub on :1234 |
| 75 | debug: all disk | 70 | debug: all system |
| 76 | qemu-system-x86_64 \ | 71 | qemu-system-x86_64 \ |
| 77 | -bios $(OVMF) \ | 72 | -bios $(OVMF) \ |
| 78 | -drive file=$(DISK_IMG),format=raw,if=none,id=disk \ | 73 | -drive file=$(SYSTEM_IMG),format=raw,if=none,id=disk \ |
| 79 | -device ide-hd,drive=disk,bus=ide.0 \ | 74 | -device ide-hd,drive=disk,bus=ide.0 \ |
| 80 | -drive file=fat:rw:$(ESP_DIR),format=raw,if=none,id=esp \ | ||
| 81 | -device ide-hd,drive=esp,bus=ide.1 \ | ||
| 82 | -serial stdio \ | 75 | -serial stdio \ |
| 83 | -m 256M \ | 76 | -m 256M \ |
| 84 | -machine q35 \ | 77 | -machine q35 \ |
| @@ -90,23 +83,27 @@ debug: all disk | |||
| 90 | # Boot from a raw disk image (delegates to boot-image.sh which builds the | 83 | # Boot from a raw disk image (delegates to boot-image.sh which builds the |
| 91 | # full chain if the image doesn't exist yet) | 84 | # full chain if the image doesn't exist yet) |
| 92 | run-image: all | 85 | run-image: all |
| 93 | ./scripts/boot-image.sh rs-dos.img | ||
| 94 | 86 | ||
| 95 | # ---- Data disk ----------------------------------------------------------- | 87 | # ---- System image -------------------------------------------------------- |
| 96 | 88 | ||
| 97 | # Create the FAT32 data disk if it doesn't exist. Uses mkfs.fat -C (the | 89 | # One dd-able FAT32 image: kernel + ESP + /etc + /bin + seed data. |
| 98 | # flag that actually creates the file) and seeds it from seed/. | 90 | # Stamp rule: rebuilt only when the kernel or this script changes — an |
| 99 | disk: | 91 | # up-to-date image is NEVER touched, so files written at runtime survive. |
| 100 | ./scripts/mkdisk.sh "$(DISK_IMG)" | 92 | system: |
| 93 | @if [ ! -f "$(SYSTEM_IMG)" ] || [ "$$(stat -c %Y $(BZIMAGE) 2>/dev/null || echo 0)" -gt "$$(stat -c %Y $(SYSTEM_IMG) 2>/dev/null || echo 0)" ]; then \ | ||
| 94 | ./scripts/mksystem.sh --force "$(SYSTEM_IMG)"; \ | ||
| 95 | else \ | ||
| 96 | echo "system image up to date: $(SYSTEM_IMG)"; \ | ||
| 97 | fi | ||
| 101 | 98 | ||
| 102 | # Re-format the data disk from scratch (wipes it, then re-seeds). | 99 | # Re-create the system image from scratch (wipes runtime data). |
| 103 | disk-fresh: | 100 | system-fresh: |
| 104 | ./scripts/mkdisk.sh --fresh "$(DISK_IMG)" | 101 | ./scripts/mksystem.sh --force "$(SYSTEM_IMG)" |
| 105 | 102 | ||
| 106 | # List the data disk contents with mtools (requires mtools). | 103 | # List the system image contents with mtools (requires mtools). |
| 107 | inspect: | 104 | inspect: |
| 108 | @command -v mdir >/dev/null 2>&1 || (echo "inspect needs mtools: sudo apt install mtools"; exit 1) | 105 | @command -v mdir >/dev/null 2>&1 || (echo "inspect needs mtools: sudo apt install mtools"; exit 1) |
| 109 | @mdir -i "$(DISK_IMG)" | 106 | @mdir -i "$(SYSTEM_IMG)" :: |
| 110 | 107 | ||
| 111 | # Full automated round trip: build -> boot -> shell commands -> poweroff | 108 | # Full automated round trip: build -> boot -> shell commands -> poweroff |
| 112 | # -> fsck + mtools verification. | 109 | # -> fsck + mtools verification. |
| @@ -152,7 +149,7 @@ fmt: | |||
| 152 | 149 | ||
| 153 | clean: | 150 | clean: |
| 154 | cargo clean | 151 | cargo clean |
| 155 | rm -rf $(ESP_DIR) rs-dos.img | 152 | rm -rf $(ESP_DIR) |
| 156 | 153 | ||
| 157 | help: | 154 | help: |
| 158 | @echo "rs-dos — Make targets" | 155 | @echo "rs-dos — Make targets" |
| @@ -163,16 +160,13 @@ help: | |||
| 163 | @echo " make image build full chain disk image (bootloader + kernel)" | 160 | @echo " make image build full chain disk image (bootloader + kernel)" |
| 164 | @echo " make boot-image build + boot full chain via QEMU" | 161 | @echo " make boot-image build + boot full chain via QEMU" |
| 165 | @echo " make run QEMU serial console (direct boot, no bootloader)" | 162 | @echo " make run QEMU serial console (direct boot, no bootloader)" |
| 166 | @echo " make run-gui QEMU with graphical window (GOP/PS2)" | 163 | @echo " make system create the system image if stale/missing (/tmp/system.img)" |
| 167 | @echo " make debug QEMU + GDB stub on :1234" | 164 | @echo " make system-fresh re-create the system image (wipes runtime data)" |
| 168 | @echo " make disk create the data disk if missing (default /tmp/disk.img)" | 165 | @echo " make inspect list system image contents (mtools)" |
| 169 | @echo " make disk-fresh re-format the data disk (wipes it)" | ||
| 170 | @echo " make inspect list data disk contents (mtools)" | ||
| 171 | @echo " make test-disk automated disk round trip (build->boot->fsck)" | 166 | @echo " make test-disk automated disk round trip (build->boot->fsck)" |
| 172 | @echo " make clean remove build artifacts" | 167 | @echo " make clean remove build artifacts" |
| 173 | @echo " make fmt format Rust source" | 168 | @echo " make fmt format Rust source" |
| 174 | @echo "" | 169 | @echo " ./scripts/mksystem.sh build the dd-able system image" |
| 175 | @echo "Scripts:" | ||
| 176 | @echo " ./scripts/boot.sh quick direct boot (dev loop)" | 170 | @echo " ./scripts/boot.sh quick direct boot (dev loop)" |
| 177 | @echo " ./scripts/mkdisk.sh create/re-format the data disk" | 171 | @echo " ./scripts/mkdisk.sh create/re-format the data disk" |
| 178 | @echo " ./scripts/test-disk.sh automated disk round trip" | 172 | @echo " ./scripts/test-disk.sh automated disk round trip" |
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 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | # | ||
| 3 | # scripts/build-image.sh — assemble a bootable rs-dos disk image (UEFI/GPT) | ||
| 4 | # | ||
| 5 | # Layout: | ||
| 6 | # Partition 1: ESP (FAT32) — rsboot bootloader + kernel | ||
| 7 | # | ||
| 8 | # Output: | ||
| 9 | # rs-dos.img (or custom path) — bootable raw disk image for UEFI | ||
| 10 | # | ||
| 11 | # Usage: | ||
| 12 | # ./scripts/build-image.sh [output.img] | ||
| 13 | # | ||
| 14 | set -euo pipefail | ||
| 15 | |||
| 16 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | ||
| 17 | PROJECT_DIR="$(dirname "$SCRIPT_DIR")" | ||
| 18 | |||
| 19 | cd "$PROJECT_DIR" | ||
| 20 | |||
| 21 | BUILD_DIR="$PROJECT_DIR/build" | ||
| 22 | ESP_DIR="$PROJECT_DIR/esp" | ||
| 23 | |||
| 24 | IMAGE="${1:-$PROJECT_DIR/rs-dos.img}" | ||
| 25 | TARGET=x86_64-unknown-uefi | ||
| 26 | PROFILE=debug | ||
| 27 | OUT_DIR="target/${TARGET}/${PROFILE}" | ||
| 28 | |||
| 29 | BOOTLOADER="$BUILD_DIR/bootloader/BOOTX64.EFI" | ||
| 30 | KERNEL="$OUT_DIR/rsdos.efi" | ||
| 31 | BZIMAGE="$ESP_DIR/bzImage" | ||
| 32 | INITRD="$ESP_DIR/initramfs.cpio.gz" | ||
| 33 | BOOT_EFI="$ESP_DIR/EFI/BOOT/BOOTX64.EFI" | ||
| 34 | |||
| 35 | ESP_START=2048 # 1 MiB offset (standard alignment) | ||
| 36 | ESP_SIZE_MB=64 | ||
| 37 | |||
| 38 | IMAGE_SIZE_MB=$((ESP_SIZE_MB + 2)) # ESP + GPT gap + backup | ||
| 39 | |||
| 40 | # ---- checks ---- | ||
| 41 | for cmd in sfdisk mkfs.vfat mcopy truncate dd; do | ||
| 42 | if ! command -v "$cmd" &>/dev/null; then | ||
| 43 | echo "Error: $cmd not found" | ||
| 44 | exit 1 | ||
| 45 | fi | ||
| 46 | done | ||
| 47 | |||
| 48 | # ---- build kernel ---- | ||
| 49 | echo "[1/4] Building kernel..." | ||
| 50 | cargo build --package rsdos --target x86_64-unknown-uefi ${CARGO_FLAGS:-} | ||
| 51 | |||
| 52 | # ---- build bootloader ---- | ||
| 53 | echo "[2/4] Building bootloader..." | ||
| 54 | if [ ! -f "$BOOTLOADER" ]; then | ||
| 55 | "$SCRIPT_DIR/build-bootloader.sh" | ||
| 56 | fi | ||
| 57 | |||
| 58 | # ---- assemble ESP ---- | ||
| 59 | echo "[3/4] Assembling ESP..." | ||
| 60 | mkdir -p "$ESP_DIR/EFI/BOOT" | ||
| 61 | cp "$KERNEL" "$BZIMAGE" | ||
| 62 | cp "$BOOTLOADER" "$BOOT_EFI" | ||
| 63 | if [ ! -f "$INITRD" ]; then | ||
| 64 | printf '' | gzip >"$INITRD" | ||
| 65 | fi | ||
| 66 | |||
| 67 | # ---- create disk image ---- | ||
| 68 | echo "[4/4] Writing disk image..." | ||
| 69 | rm -f "$IMAGE" | ||
| 70 | truncate -s "${IMAGE_SIZE_MB}M" "$IMAGE" | ||
| 71 | |||
| 72 | # GPT partition table: single EFI System partition | ||
| 73 | sfdisk --quiet "$IMAGE" <<EOF | ||
| 74 | label: gpt | ||
| 75 | type=U, start=$ESP_START, size=+ | ||
| 76 | EOF | ||
| 77 | |||
| 78 | # Build FAT32 image with ESP contents | ||
| 79 | ESP_IMG=$(mktemp) | ||
| 80 | truncate -s "${ESP_SIZE_MB}M" "$ESP_IMG" | ||
| 81 | mkfs.vfat -F 32 -n "RS-DOS" "$ESP_IMG" >/dev/null 2>&1 | ||
| 82 | |||
| 83 | # Copy files into FAT | ||
| 84 | mmd -i "$ESP_IMG" ::/EFI 2>/dev/null || true | ||
| 85 | mmd -i "$ESP_IMG" ::/EFI/BOOT 2>/dev/null || true | ||
| 86 | mcopy -i "$ESP_IMG" "$BOOT_EFI" ::/EFI/BOOT/BOOTX64.EFI | ||
| 87 | mcopy -i "$ESP_IMG" "$BZIMAGE" ::/bzImage 2>/dev/null || true | ||
| 88 | mcopy -i "$ESP_IMG" "$INITRD" ::/initramfs.cpio.gz 2>/dev/null || true | ||
| 89 | |||
| 90 | # Inject FAT into image at partition offset | ||
| 91 | dd if="$ESP_IMG" of="$IMAGE" bs=512 seek="$ESP_START" conv=notrunc 2>/dev/null | ||
| 92 | rm -f "$ESP_IMG" | ||
| 93 | |||
| 94 | echo "" | ||
| 95 | echo "=== Done: $IMAGE ===" | ||
| 96 | ls -lh "$IMAGE" | ||
| 97 | echo "" | ||
| 98 | echo "Boot with: ./scripts/boot-image.sh" | ||
| 99 | 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 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | # | ||
| 3 | # scripts/mkdisk.sh — create (or re-create) the FAT32 data disk that the | ||
| 4 | # kernel mounts at /disk. | ||
| 5 | # | ||
| 6 | # Usage: | ||
| 7 | # ./scripts/mkdisk.sh # create /tmp/disk.img (64M) if missing | ||
| 8 | # ./scripts/mkdisk.sh --fresh # re-format (wipes the image!) | ||
| 9 | # ./scripts/mkdisk.sh --size 128M # different size (default 64M) | ||
| 10 | # ./scripts/mkdisk.sh /path/img # custom image path | ||
| 11 | # ./scripts/mkdisk.sh --no-seed # skip the seed/ directory | ||
| 12 | # ./scripts/mkdisk.sh --label CRASHLOG # volume label (default RSDOS) | ||
| 13 | # | ||
| 14 | # The image is created with `mkfs.fat -C`, which is the option that | ||
| 15 | # actually creates the file — plain `mkfs.fat` only writes into an | ||
| 16 | # existing one. Files from seed/ (if present) are copied in with mtools. | ||
| 17 | # | ||
| 18 | set -euo pipefail | ||
| 19 | |||
| 20 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | ||
| 21 | PROJECT_DIR="$(dirname "$SCRIPT_DIR")" | ||
| 22 | |||
| 23 | IMG="/tmp/disk.img" | ||
| 24 | SIZE="64M" | ||
| 25 | FRESH=0 | ||
| 26 | SEED_DIR="$PROJECT_DIR/seed" | ||
| 27 | LABEL="RSDOS" | ||
| 28 | |||
| 29 | while [ $# -gt 0 ]; do | ||
| 30 | case "$1" in | ||
| 31 | --fresh) FRESH=1 ;; | ||
| 32 | --size) | ||
| 33 | shift | ||
| 34 | SIZE="$1" | ||
| 35 | ;; | ||
| 36 | --no-seed) SEED_DIR="" ;; | ||
| 37 | --label) | ||
| 38 | shift | ||
| 39 | LABEL="$1" | ||
| 40 | ;; | ||
| 41 | --help | -h) | ||
| 42 | head -20 "$0" | grep "^#" | sed 's/^# \?//' | ||
| 43 | exit 0 | ||
| 44 | ;; | ||
| 45 | *) | ||
| 46 | IMG="$1" | ||
| 47 | ;; | ||
| 48 | esac | ||
| 49 | shift | ||
| 50 | done | ||
| 51 | |||
| 52 | if [ -f "$IMG" ] && [ "$FRESH" -eq 0 ]; then | ||
| 53 | echo "mkdisk: $IMG already exists (use --fresh to re-format)" | ||
| 54 | exit 0 | ||
| 55 | fi | ||
| 56 | |||
| 57 | if ! command -v mkfs.fat >/dev/null 2>&1; then | ||
| 58 | echo "mkdisk: mkfs.fat not found — install dosfstools (sudo apt install dosfstools)" >&2 | ||
| 59 | exit 1 | ||
| 60 | fi | ||
| 61 | |||
| 62 | # mkfs.fat sizes are 1024-byte blocks; accept M/G/K suffixes too. | ||
| 63 | case "$SIZE" in | ||
| 64 | *G) SIZE_BLOCKS=$((${SIZE%G} * 1024 * 1024)) ;; | ||
| 65 | *M) SIZE_BLOCKS=$((${SIZE%M} * 1024)) ;; | ||
| 66 | *K) SIZE_BLOCKS=$((${SIZE%K})) ;; | ||
| 67 | *) SIZE_BLOCKS="$SIZE" ;; | ||
| 68 | esac | ||
| 69 | if [ -z "$SIZE_BLOCKS" ] || [ "$SIZE_BLOCKS" -le 0 ] 2>/dev/null; then | ||
| 70 | echo "mkdisk: bad size '$SIZE' (use e.g. 64M or 65536)" >&2 | ||
| 71 | exit 1 | ||
| 72 | fi | ||
| 73 | |||
| 74 | echo "mkdisk: creating $IMG ($SIZE FAT32, label $LABEL)" | ||
| 75 | rm -f "$IMG" | ||
| 76 | mkfs.fat -C -F 32 -n "$LABEL" "$IMG" "$SIZE_BLOCKS" | ||
| 77 | |||
| 78 | if [ -n "$SEED_DIR" ] && [ -d "$SEED_DIR" ]; then | ||
| 79 | if ! command -v mcopy >/dev/null 2>&1; then | ||
| 80 | echo "mkdisk: warning: mcopy not found — skipping seed files (install mtools)" >&2 | ||
| 81 | else | ||
| 82 | echo "mkdisk: seeding from $SEED_DIR/" | ||
| 83 | for f in "$SEED_DIR"/*; do | ||
| 84 | [ -f "$f" ] || continue | ||
| 85 | mcopy -i "$IMG" "$f" "::/$(basename "$f")" | ||
| 86 | echo " -> $(basename "$f")" | ||
| 87 | done | ||
| 88 | fi | ||
| 89 | fi | ||
| 90 | |||
| 91 | 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 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | # | ||
| 3 | # scripts/mksystem.sh — assemble the single dd-able rs-dos system image | ||
| 4 | # | ||
| 5 | # Replaces the old two-source setup (vvfat `esp/` directory for boot + | ||
| 6 | # separate /tmp/disk.img for data). One FAT32 image holds EVERYTHING: | ||
| 7 | # | ||
| 8 | # \bzImage kernel (rsbl requires this exact path) | ||
| 9 | # \initramfs.cpio.gz empty initramfs (rsbl requires it) | ||
| 10 | # \EFI\BOOT\BOOTX64.EFI boot entry: the kernel (default) or rsboot | ||
| 11 | # \etc\motd.txt seed config | ||
| 12 | # \bin\cli\ \bin\gui\ binary tree (empty until Phase 2/4 migrate tools) | ||
| 13 | # \*.txt seed files (seed/) | ||
| 14 | # | ||
| 15 | # The firmware boots from this image and the kernel mounts the same | ||
| 16 | # volume at /disk/<UID>/ — boot medium and data medium are one disk. | ||
| 17 | # | ||
| 18 | # Usage: | ||
| 19 | # ./scripts/mksystem.sh [options] [output.img] # default /tmp/system.img | ||
| 20 | # | ||
| 21 | # Options: | ||
| 22 | # --force rebuild even if the image exists (Makefile stamps handle | ||
| 23 | # normal freshness; without --force an existing image is kept, | ||
| 24 | # so user files survive across reboots) | ||
| 25 | # --size NM image size in MB (default 64) | ||
| 26 | # --chain \EFI\BOOT\BOOTX64.EFI = rsboot bootloader (full chain: | ||
| 27 | # firmware → rsboot → rsnix). Default is the kernel itself | ||
| 28 | # (direct: firmware → rsnix), matching the old dev loop. | ||
| 29 | # --gpt wrap the FAT volume in a GPT partition table (for dd-ing | ||
| 30 | # to real USB sticks whose firmware refuses superfloppies) | ||
| 31 | # --label NAME FAT volume label (default RSDOS) | ||
| 32 | # | ||
| 33 | set -euo pipefail | ||
| 34 | |||
| 35 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | ||
| 36 | PROJECT_DIR="$(dirname "$SCRIPT_DIR")" | ||
| 37 | cd "$PROJECT_DIR" | ||
| 38 | |||
| 39 | IMG="/tmp/system.img" | ||
| 40 | SIZE_MB=64 | ||
| 41 | FORCE=0 | ||
| 42 | CHAIN=0 | ||
| 43 | GPT=0 | ||
| 44 | LABEL="RSDOS" | ||
| 45 | |||
| 46 | while [ $# -gt 0 ]; do | ||
| 47 | case "$1" in | ||
| 48 | --force | --fresh) | ||
| 49 | FORCE=1 | ||
| 50 | ;; | ||
| 51 | --size) | ||
| 52 | shift | ||
| 53 | SIZE_MB="$1" | ||
| 54 | ;; | ||
| 55 | --chain) | ||
| 56 | CHAIN=1 | ||
| 57 | ;; | ||
| 58 | --gpt) | ||
| 59 | GPT=1 | ||
| 60 | ;; | ||
| 61 | --label) | ||
| 62 | shift | ||
| 63 | LABEL="$1" | ||
| 64 | ;; | ||
| 65 | --help | -h) | ||
| 66 | head -30 "$0" | grep "^#" | sed 's/^# \?//' | ||
| 67 | exit 0 | ||
| 68 | ;; | ||
| 69 | -*) | ||
| 70 | echo "mksystem: unknown option: $1" >&2 | ||
| 71 | exit 1 | ||
| 72 | ;; | ||
| 73 | *) | ||
| 74 | IMG="$1" | ||
| 75 | ;; | ||
| 76 | esac | ||
| 77 | shift | ||
| 78 | done | ||
| 79 | |||
| 80 | TARGET=x86_64-unknown-uefi | ||
| 81 | PROFILE=debug | ||
| 82 | OUT_DIR="target/$TARGET/$PROFILE" | ||
| 83 | KERNEL="$OUT_DIR/rsdos.efi" | ||
| 84 | BZIMAGE="esp/bzImage" | ||
| 85 | INITRD="esp/initramfs.cpio.gz" | ||
| 86 | BOOTLOADER="build/bootloader/BOOTX64.EFI" | ||
| 87 | |||
| 88 | # ---- checks ---- | ||
| 89 | for cmd in mkfs.fat mcopy mmd; do | ||
| 90 | if ! command -v "$cmd" >/dev/null 2>&1; then | ||
| 91 | echo "mksystem: $cmd not found (install dosfstools + mtools)" >&2 | ||
| 92 | exit 1 | ||
| 93 | fi | ||
| 94 | done | ||
| 95 | if [ "$GPT" -eq 1 ]; then | ||
| 96 | for cmd in sfdisk truncate dd; do | ||
| 97 | if ! command -v "$cmd" >/dev/null 2>&1; then | ||
| 98 | echo "mksystem: --gpt needs $cmd" >&2 | ||
| 99 | exit 1 | ||
| 100 | fi | ||
| 101 | done | ||
| 102 | fi | ||
| 103 | |||
| 104 | if [ -f "$IMG" ] && [ "$FORCE" -eq 0 ]; then | ||
| 105 | echo "mksystem: $IMG already exists (use --force to re-create)" | ||
| 106 | exit 0 | ||
| 107 | fi | ||
| 108 | |||
| 109 | # ---- build kernel ---- | ||
| 110 | if [ ! -f "$KERNEL" ]; then | ||
| 111 | echo "mksystem: building kernel..." | ||
| 112 | cargo build --package rsdos --target "$TARGET" | ||
| 113 | fi | ||
| 114 | |||
| 115 | # ---- build bootloader (chain mode only) ---- | ||
| 116 | if [ "$CHAIN" -eq 1 ] && [ ! -f "$BOOTLOADER" ]; then | ||
| 117 | echo "mksystem: building bootloader..." | ||
| 118 | "$SCRIPT_DIR/build-bootloader.sh" | ||
| 119 | fi | ||
| 120 | |||
| 121 | # ---- deploy kernel artifacts to esp/ (shared with the old layout) ---- | ||
| 122 | mkdir -p esp/EFI/BOOT | ||
| 123 | cp "$KERNEL" "$BZIMAGE" | ||
| 124 | [ -f "$INITRD" ] || printf '' | gzip >"$INITRD" | ||
| 125 | |||
| 126 | # ---- create the FAT volume ---- | ||
| 127 | rm -f "$IMG" | ||
| 128 | SIZE_BLOCKS=$((SIZE_MB * 1024)) # mkfs.fat counts 1024-byte blocks | ||
| 129 | echo "mksystem: creating $IMG (${SIZE_MB}M FAT32, label $LABEL)" | ||
| 130 | mkfs.fat -C -F 32 -n "$LABEL" "$IMG" "$SIZE_BLOCKS" >/dev/null | ||
| 131 | |||
| 132 | echo "mksystem: staging system tree" | ||
| 133 | mmd -i "$IMG" ::/EFI ::/EFI/BOOT ::/etc ::/bin ::/bin/cli ::/bin/gui | ||
| 134 | mcopy -i "$IMG" "$BZIMAGE" ::/bzImage | ||
| 135 | mcopy -i "$IMG" "$INITRD" ::/initramfs.cpio.gz | ||
| 136 | if [ "$CHAIN" -eq 1 ]; then | ||
| 137 | mcopy -i "$IMG" "$BOOTLOADER" ::/EFI/BOOT/BOOTX64.EFI | ||
| 138 | echo " \\EFI\\BOOT\\BOOTX64.EFI <- rsboot (full chain)" | ||
| 139 | else | ||
| 140 | mcopy -i "$IMG" "$KERNEL" ::/EFI/BOOT/BOOTX64.EFI | ||
| 141 | echo " \\EFI\\BOOT\\BOOTX64.EFI <- kernel (direct)" | ||
| 142 | fi | ||
| 143 | echo " \\bzImage \\initramfs.cpio.gz \\bin{\\cli,\\gui} \\etc" | ||
| 144 | |||
| 145 | # Seed files: motd lives in \etc, everything else at the volume root. | ||
| 146 | SEED_DIR="$PROJECT_DIR/seed" | ||
| 147 | if [ -d "$SEED_DIR" ]; then | ||
| 148 | for f in "$SEED_DIR"/*; do | ||
| 149 | [ -f "$f" ] || continue | ||
| 150 | name="$(basename "$f")" | ||
| 151 | if [ "$name" = "motd.txt" ]; then | ||
| 152 | mcopy -i "$IMG" "$f" ::/etc/"$name" | ||
| 153 | echo " \\etc\\$name" | ||
| 154 | else | ||
| 155 | mcopy -i "$IMG" "$f" ::/"$name" | ||
| 156 | echo " \\$name" | ||
| 157 | fi | ||
| 158 | done | ||
| 159 | fi | ||
| 160 | |||
| 161 | # ---- optional GPT wrapper (real-hardware dd target) ---- | ||
| 162 | if [ "$GPT" -eq 1 ]; then | ||
| 163 | GPT_START=2048 # 1 MiB offset (standard alignment) | ||
| 164 | GPT_IMG="$(mktemp)" | ||
| 165 | truncate -s "${SIZE_MB}M" "$GPT_IMG" | ||
| 166 | dd if="$IMG" of="$GPT_IMG" bs=512 seek="$GPT_START" conv=notrunc 2>/dev/null | ||
| 167 | FINAL="$(mktemp)" | ||
| 168 | truncate -s "$((SIZE_MB + 2))M" "$FINAL" | ||
| 169 | sfdisk --quiet "$FINAL" <<EOF | ||
| 170 | label: gpt | ||
| 171 | type=U, start=$GPT_START, size= | ||
| 172 | EOF | ||
| 173 | dd if="$GPT_IMG" of="$FINAL" bs=512 seek="$GPT_START" conv=notrunc 2>/dev/null | ||
| 174 | rm -f "$GPT_IMG" | ||
| 175 | mv "$FINAL" "$IMG" | ||
| 176 | echo "mksystem: wrapped in GPT (partition at LBA $GPT_START)" | ||
| 177 | fi | ||
| 178 | |||
| 179 | echo "" | ||
| 180 | echo "=== Done: $IMG ===" | ||
| 181 | ls -lh "$IMG" | ||
| 182 | echo "Boot with: make run (or: make boot-image)" | ||
| 183 | [ "$GPT" -eq 0 ] && echo "USB dd tip: add --gpt for firmwares that refuse superfloppies" | ||
| 184 | 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): | |||
| 72 | else: | 72 | else: |
| 73 | sys.stderr.write("warning: no sendkey mapping for %r\n" % ch) | 73 | sys.stderr.write("warning: no sendkey mapping for %r\n" % ch) |
| 74 | continue | 74 | continue |
| 75 | time.sleep(0.05) | 75 | time.sleep(0.25) |
| 76 | sendkey(sock, "ret") | 76 | sendkey(sock, "ret") |
| 77 | time.sleep(0.7) | 77 | time.sleep(1.5) |
| 78 | 78 | ||
| 79 | 79 | ||
| 80 | def main(): | 80 | 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 | |||
| 26 | 26 | ||
| 27 | OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" | 27 | OVMF="${OVMF:-/usr/share/edk2/x64/OVMF.4m.fd}" |
| 28 | QEMU="${QEMU:-qemu-system-x86_64}" | 28 | QEMU="${QEMU:-qemu-system-x86_64}" |
| 29 | IMG="${DISK_IMG:-/tmp/disk.img}" | 29 | IMG="${SYSTEM_IMG:-/tmp/system.img}" |
| 30 | ESP_DIR="$PROJECT_DIR/esp" | 30 | ESP_DIR="$PROJECT_DIR/esp" |
| 31 | QPID=/tmp/rsdos-test-qpid | 31 | QPID=/tmp/rsdos-test-qpid |
| 32 | SOCK=/tmp/rsdos-test-qmon.sock | 32 | SOCK=/tmp/rsdos-test-qmon.sock |
| @@ -69,29 +69,29 @@ wait_qemu_exit() { | |||
| 69 | echo "==> 1/6 build kernel (debug)" | 69 | echo "==> 1/6 build kernel (debug)" |
| 70 | cargo build --package rsdos --target x86_64-unknown-uefi | 70 | cargo build --package rsdos --target x86_64-unknown-uefi |
| 71 | 71 | ||
| 72 | echo "==> 2/6 deploy to ESP + ensure disk image" | 72 | echo "==> 2/6 ensure system image" |
| 73 | mkdir -p "$ESP_DIR/EFI/BOOT" | ||
| 74 | cp target/x86_64-unknown-uefi/debug/rsdos.efi "$ESP_DIR/EFI/BOOT/BOOTX64.EFI" | ||
| 75 | if [ "$FRESH" -eq 1 ]; then | 73 | if [ "$FRESH" -eq 1 ]; then |
| 76 | "$SCRIPT_DIR/mkdisk.sh" --fresh "$IMG" | 74 | "$SCRIPT_DIR/mksystem.sh" --force "$IMG" |
| 77 | else | 75 | else |
| 78 | "$SCRIPT_DIR/mkdisk.sh" "$IMG" | 76 | "$SCRIPT_DIR/mksystem.sh" "$IMG" |
| 79 | fi | 77 | fi |
| 80 | 78 | ||
| 81 | echo "==> 3/6 boot QEMU (headless)" | 79 | echo "==> 3/6 boot QEMU (headless)" |
| 82 | kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true | 80 | kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true |
| 83 | sleep 1 | 81 | sleep 1 |
| 84 | rm -f "$SOCK" "$LOG" | 82 | rm -f "$SOCK" "$LOG" |
| 85 | "$QEMU" -bios "$OVMF" \ | 83 | # Throwaway OVMF varstore: a stale/poisoned NvVars (e.g. left by the old |
| 84 | # vvfat setup) makes the firmware skip the \EFI\BOOT fallback entirely. | ||
| 85 | NV="/tmp/rsdos-test-ovmf-vars.fd" | ||
| 86 | rm -f "$NV" | ||
| 87 | cp /usr/share/edk2/x64/OVMF.4m.fd "$NV" | ||
| 88 | "$QEMU" -bios "$NV" \ | ||
| 86 | -drive file="$IMG",format=raw,if=none,id=disk \ | 89 | -drive file="$IMG",format=raw,if=none,id=disk \ |
| 87 | -device ide-hd,drive=disk,bus=ide.0 \ | 90 | -device ide-hd,drive=disk,bus=ide.0 \ |
| 88 | -drive file=fat:rw:"$ESP_DIR",format=raw,if=none,id=esp \ | ||
| 89 | -device ide-hd,drive=esp,bus=ide.1 \ | ||
| 90 | -serial file:"$LOG" \ | 91 | -serial file:"$LOG" \ |
| 91 | -monitor unix:"$SOCK",server,nowait \ | 92 | -monitor unix:"$SOCK",server,nowait \ |
| 92 | -m 256M -machine q35 -cpu qemu64 -no-reboot -display none \ | 93 | -m 256M -machine q35 -cpu qemu64 -no-reboot -display none \ |
| 93 | >"$QLOG" 2>&1 & | 94 | >"$QLOG" 2>&1 & |
| 94 | echo $! >"$QPID" | ||
| 95 | 95 | ||
| 96 | wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" | 96 | wait_log "$LOG" "boot: entering shell" 90 || fail "kernel never reached the shell" |
| 97 | sleep 1 | 97 | sleep 1 |
