3.6 KiB
The release ISO — flashable boot media
zig build release-x86-64 produces zig-out/danos-x86-64.iso, the file you
hand to someone who wants to try danos on a real machine: point
balenaEtcher (or Raspberry Pi Imager, or plain dd)
at it, flash a USB stick, and boot the stick. The same file also burns to
optical media. zig build check-iso-image validates it without booting.
zig build release-x86-64
# Etcher: select danos-x86-64.iso → select the stick → Flash
# or: sudo dd if=zig-out/danos-x86-64.iso of=/dev/rdiskN bs=4m (macOS; triple-check N)
Why an ISO when danos-usb.img already boots
danos-usb.img is a raw FAT32 superfloppy — a filesystem starting at
sector 0, no partition table. UEFI firmware accepts that from a USB stick (it
probes whole-disk FAT before giving up), which is why dd-ing the .img works
and why QEMU and the test harness boot it directly. But it is a
developer-shaped artifact: flashing apps expect an ISO, and a superfloppy
can't be burned to a CD/DVD or carry a partition table for pickier firmware.
The ISO wraps that same FAT image — bit-identical, built by the same
tools/make-fat-image.py — in a container that boots everywhere release media
gets consumed. One payload, two images: the .img stays the raw volume the QEMU
harness mounts and boots, the .iso is what leaves the building.
How a hybrid ISO boots twice
The trick (the same one Linux distribution ISOs use, usually via xorriso -isohybrid…) is that ISO9660 reserves its first 32 KiB as a system area it
never touches — exactly where an MBR lives on a disk. So one file can carry two
tables of contents, both pointing at the same embedded FAT image:
- Flashed to USB (Etcher, dd): firmware sees a disk whose sector 0 is an
MBR with one partition of type
0xEF(EFI System Partition) covering the embedded FAT image. It mounts that ESP and runs\EFI\BOOT\BOOTX64.efi— the standard removable-media path (efi.md). - Burned to optical media: firmware reads the ISO9660 volume descriptors
at sector 16 and finds an El Torito boot record. Its catalog has one
entry, platform ID
0xEF(EFI), whose start LBA is — again — the embedded FAT image. The firmware exposes that image as a virtual disk and runs the sameBOOTX64.efioff it.
Neither path involves the legacy BIOS boot-sector machinery: danos is UEFI-only (system-requirements.md), so the MBR holds no boot code, just the partition entry, and the El Torito entry is EFI-class, not floppy emulation.
One El Torito wrinkle: the catalog's sector-count field is 16-bit (units of 512 bytes), so it can name at most 32 MiB — less than the 64 MiB FAT image. That is fine in practice: firmware sizes the FAT filesystem from its own BPB, and the boot files sit in the first few MiB of the image (clusters are allocated from the front) either way. The USB path has no such cap.
The builder
tools/make-iso-image.py follows the house rule of
make-fat-image.py: pure Python 3 standard
library, no external tools (no xorriso, mkisofs, or isohybrid), with a
--verify mode the check-iso-image step runs — it checks that the MBR
partition and the El Torito catalog agree on where the FAT image lives and
that a FAT32 boot sector is actually there. Every timestamp field in the ISO
is zeroed, so the build is reproducible byte-for-byte.
The ISO9660 filesystem around the boot machinery is minimal but real: a root
directory listing BOOT.CAT (the catalog) and EFI.IMG (the FAT image), so
file, mount tools, and archive browsers can open the ISO and see what's in
it.