# Boot images and stage0 [Documentation index](README.md) · [First build](getting-started.md) · [Implementation status](implementation-status.md) FDS keeps the machine's boot files on internal NVMe and its operating system on a removable SYSTEM cartridge. The Pi firmware loads the kernel and a small initial filesystem. Its Rust `fds-stage0` program finds the partition named `FDS_SYSTEM`, mounts it read-only, and hands control to native s6. Device names such as `sda` are never used as cartridge identities. The software boot path has passed on an ARM virtual machine using the actual FDS Pi kernel. That does not verify Pi firmware, NVMe, RP1, display output, USB power or physical boot speed. Those checks are deferred until the machine is assembled. M5 now provides an ordinary-user `FDS>` console with a temporary home and measured boot events. Password login remains locked; this is an intentional local single-user session. The M2 test VM separately offers a temporary root development shell. See [native init](init.md) and [performance reports](performance.md). ## Build on the workstation Use the [Arch host setup](build-host.md) first. In addition to its original tools, install host `lz4`, `zstd`, `util-linux`, `e2fsprogs` and `libarchive` if missing. They supply initramfs compression, independent GPT inspection and the initial VM's filesystem/archive tools; these are build/test dependencies. Python 3.14+ reads zstd-compressed XBPS archives. Filesystem builders are obtained as verified Void packages in `.host/image-tools`, separate from the build container. From the repository root, run sequentially: ```sh make bootstrap make rootfs PROFILE=cli make initramfs make system-card PROFILE=cli make boot-volume make rootfs-test make init-test make boot-test ``` The rootfs build includes the FDS CLI, base Dasung daemon, native s6 database, and the kernel's matching modules. Its first kernel cross-build is substantial; progress is recorded in `out/logs/xbps-fds-kernel.log`. `make kernel` also builds that kernel independently. Later calls reuse its package only when recorded source inputs and every exported payload hash still match. To force a kernel rebuild, run `./tools/build-kernel --rebuild`. This source/payload cache is not yet a complete lock of rolling binary build dependencies. No command here opens or writes a physical target disk. On a new build host, `make init-test` installs QEMU's system emulator into the project-local Void environment before `make boot-test` uses it. | Output | Meaning | | --- | --- | | `out/kernel/boot/kernel_2712.img` | Pi 5 kernel with boot-critical drivers built in | | `out/kernel/usr/lib/modules/` | Matching optional drivers | | `out/fds-initramfs.img` | Uncompressed default initramfs; a symlink to a versioned build | | `out/initramfs/initramfs.cpio*` | Uncompressed, gzip, legacy-LZ4 and Zstandard variants | | `out/rootfs-aarch64.tar` | Configured userspace archive with target ownership and capabilities | | `out/fds-system-cli.img` | Whole GPT cartridge image, containing an EROFS `FDS_SYSTEM` partition | | `out/fds-boot.img` | **512 MiB FAT32 partition image**, labeled `FDS_BOOT`; not a whole NVMe disk | | `out/boot-volume/manifest.json` | Boot image hash, firmware input and every boot file hash | | `out/m4-vm-latest/` | Logs and exact inputs from the last passing full boot matrix | `make internal-image` combines BOOT, independent RECOVERY and INTERNAL settings into `out/fds-internal.img`; see [Internal storage](internal-storage.md) for the full procedure. The FAT partition image alone is not that complete disk layout. Use the [media tools](media-tools.md) for confirmed removable-cartridge writes. ## What stage0 does The initial filesystem contains static-musl stage0 and Dasung executables, configuration, the monitor's EDID, mountpoint directories and `/dev/console`. There is no shell, BusyBox, Python, glibc, or udev in it. Stage0 mounts proc, sysfs and devtmpfs itself and binds its kernel-event socket before scanning devices. If a single SYSTEM partition is present, stage0 verifies its block-device identity, mounts EROFS read-only, and checks the FDS identity and native init executables. It stops and reaps the early Dasung process before the base service starts, moves the kernel filesystems into the new root, removes the old in-memory root, and executes the s6 launcher. It refuses this path outside root PID 1 on ramfs/tmpfs. Without SYSTEM, the console displays a missing-media prompt and keeps processing device events. Inserting media resumes boot without rebooting or polling sleeps. When more than one matching partition is visible, stage0 refuses to choose one. Its waiting console accepts: ```text list Show the discovered kernel partition identities rescan Repeat discovery immediately recovery Select FDS_RECOVERY instead of FDS_SYSTEM reboot Reboot from early userspace poweroff Power off from early userspace ``` An invalid filesystem or incomplete FDS root remains at the prompt. `list` is a diagnostic view and can show technical device paths; physical bay labels arrive with the cartridge daemon. Version 1 uses explicit recovery rather than an interactive choice between multiple SYSTEMs. Detection applies to devices visible at selection time; a cartridge inserted after root handoff cannot replace root. ## Pi display and development output `image/pi5/config.txt` selects the Pi 5 kernel, initramfs, external PCIe and VC4 KMS, disables HDMI audio, and avoids automatic camera/DSI setup. It does not overclock the Pi or PCIe link. The pinned Pi kernel retains 16 KiB pages and disables the CH341 SPI driver required by the Dasung protection policy. The production command line uses the display console and quiet kernel output. It loads the Paperlike 13K's recorded EDID from the initramfs before SYSTEM exists. The unqualified `drm.edid_firmware` setting applies this EDID to connected DRM outputs: this image is intended for the user's dedicated Dasung display. The actual adapter, connector, mode acceptance and picture still need Pi validation. Early USB control and the later base daemon share the same monitor identity. For serial diagnosis, build: ```sh make boot-volume BOOT_MODE=development ``` This replaces the convenience output link with a separate development build; previous versioned directories remain. Development enables kernel/daemon output and makes the Pi 5 `ttyAMA10` debug UART the primary userspace console. That is the Pi 5 debug connector, not an assumption about the 40-pin GPIO UART. Rebuild with `BOOT_MODE=production` to restore the display-console configuration. `fds.boot=recovery` on the kernel command line selects `FDS_RECOVERY` immediately. `fds.emulator=1` is reserved for the [workstation emulator](workstation.md): the SYSTEM loader requires QEMU virt and its fixed xHCI controller before enabling the twelve virtual bay mappings. Default Pi boot is unchanged. `fds.debug=1` exposes the early controller's diagnostics. Duplicate or unknown `fds.*` options fail clearly rather than being silently ignored. ## What the automated test proves `make boot-test` appends a test-only checker to a copy of the exported rootfs; the original stage-2 script and compiled service graph still run. It checks: - All four initramfs formats reach native s6 on read-only EROFS. - Missing SYSTEM waits; a virtual USB insertion resumes boot. - Two SYSTEM partitions remain unselected; the console can enter recovery. - Explicit recovery mode selects RECOVERY even when SYSTEM is also present. - Invalid filesystems and foreign OS roots are refused; recovery remains usable. - Kernel page size, root ownership, ping capability, temporary filesystems, single Dasung ownership, service start/restart/stop and orderly power-off. - Input disk hashes remain unchanged; the VM has no network or host-device access. QEMU uses its `max` ARM CPU because the Pi kernel needs 16 KiB page support; the earlier M2 Cortex-A72 fixture cannot run this kernel. These are compatibility and behavior checks, not Pi performance measurements. Test RECOVERY is a relabeled fixture for selection; the independent recovery product is tracked separately. Reference material: [Pi kernel builds](https://www.raspberrypi.com/documentation/computers/linux_kernel.html), [boot configuration](https://www.raspberrypi.com/documentation/computers/config_txt.html), [Pi UARTs](https://www.raspberrypi.com/documentation/computers/configuration.html#configuring-uarts), and [Linux initramfs](https://docs.kernel.org/filesystems/ramfs-rootfs-initramfs.html). ## Use the ordinary FDS console After building the CLI rootfs, initramfs and SYSTEM image: ```sh make console-vm ``` The ARM virtual machine boots the real stage0 and read-only SYSTEM image. At `FDS>` you are the ordinary `fds` user (UID 1000), with a temporary writable home. Try `fds info`, `fds inspect /FDS/CARTRIDGE.TOML`, and `fds boot-profile`. The console deliberately uses local autologin; both account passwords are locked. Use `fds poweroff` for an orderly shutdown through native s6. Enter `exit` to restart the console. Press **Ctrl-a, then x** to close QEMU immediately. Closing QEMU this way ends the test immediately; it is not a graceful-shutdown test. No host USB devices or physical disks are attached to this VM. Run `make console-test` for automated console/readiness checks and `make performance-test` for the software optimization comparison. Read [M5 validation](m5-validation.md) and [measurement boundaries](performance.md) before interpreting VM timing as hardware evidence.