A minimal real-mode → protected-mode boot chain written entirely in Forge —
no inline assembly anywhere. It is the reference implementation of the
x86_32 raw kernel target and lives in
examples/os32/.
| Stage | Source | Output | Sectors (LBA) | Loaded at |
|---|---|---|---|---|
| 1 — boot sector | src/boot/boot.dev |
build/boot.bin |
0 | 0x7C00 (BIOS) |
| 2 — loader | src/boot/loader.dev |
build/loader.raw |
1–2 | 0x9000 |
| 3 — kernel | src/kernel/kernel.dev |
build/kernel.raw |
3–10 | 0x100000 (physical) |
_dev_bios_disk_read (INT 13h CHS)
and far-jumps to the loader with _dev_jump.0x8000 via
INT 13h AH=42h (_dev_bios_disk_read_lba) — SeaBIOS’s AH=42h PIO transfer
addresses its buffer through a 16-bit ES:DI pair, so it physically cannot
write above the low 1 MiB. The buffer at 0x100000 would silently wrap._dev_enter_pmode then enables A20, installs a flat 4 GB GDT, sets
CR0.PE, and far-jumps through selector 0x08 to a 32-bit trampoline
that rep movsd’s the staging buffer from 0x8000 to 0x100000,
parks the stack at 0x70000, and jumps to the kernel at its LOAD
address (passed as lo/hi words).cargo build --release
./examples/os32/build.sh # assemble build/os32.img
./examples/os32/build.sh run # ...and boot it in QEMU
qemu-system-x86_64 -accel tcg,thread=multi \
-drive file=examples/os32/build/os32.img,format=raw,if=ide -nographic
Expected output:
ForgeOS32: loading kernel: ok
ForgeOS32: hello from 32-bit protected mode
conv mem: 0009FC00 B
The machine cli; hlts after printing.
os32-loader.fld — ARCH x86_16 / FORMAT flat / LOAD 0x9000os32-kernel.fld — ARCH x86_32 / FORMAT raw / ENTRY _start / LOAD 0x100000A standalone, byte-identical descriptor for the kernel alone lives at
examples/targets/x86_32-raw.fld:
./target/release/forgec kernel.dev -o kernel.raw --linker examples/targets/x86_32-raw.fld
The integration test os32_boot_chain_reaches_kernel boots the full
three-stage chain in QEMU and asserts the 32-bit banner; it needs
qemu-system-x86_64 on PATH.