71 lines
3.6 KiB
Markdown
71 lines
3.6 KiB
Markdown
# 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](https://etcher.balena.io) (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](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
|
|
same `BOOTX64.efi` off it.
|
|
|
|
Neither path involves the legacy BIOS boot-sector machinery: danos is
|
|
UEFI-only ([system-requirements.md](../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](../../tools/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.
|