# M2 native init validation Development reference and historical context. For current operating instructions, use the [user manual](../README.md). Acceptance applies only to the source and artifacts identified in each record. [Documentation index](README.md) · [Boot and usage](init.md) · [Roadmap](roadmap.md) Validated on **2026-09-21 (Asia/Shanghai)** on the x86_64 Arch workstation. M2's acceptance criteria passed in a complete ARM virtual machine: native s6 owns PID 1, and `s6-rc change` controls an actual test service. This is a generic ARM boot result, not a Raspberry Pi boot or performance measurement. ## Recorded environment and outputs | Item | Result | | --- | --- | | Workstation | Arch Linux x86_64, Linux 7.2.6-arch2-1, Python 3.14.7 | | Void source pin | `02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1` | | Init packages | s6-linux-init 1.2.0.1_1, s6 2.15.0.0_1, s6-rc 0.6.1.1_1 | | Emulation | QEMU system/user ARM 11.1.1_1; PRoot 5.2.0_1 for build-time child execution | | VM | ARM `virt`, Cortex-A72 software emulation, 2 CPUs, 1 GiB RAM, AHCI disk, serial console | | Generic test kernel | Signed Void `linux6.12-6.12.110_1.aarch64.xbps`; SHA-256 pinned in `config/vm-test.conf` | | Image creator | mke2fs 1.47.4, libarchive tar import; no privileged mount | | Final rootfs build | `out/rootfs-build.gSQUvr/`, built `2026-09-20T18:14:46Z` | | Rootfs archive | 346,808,320 bytes; 109 configured packages; 1,167 ARM ELF paths | | Successful boot test | `out/m2-vm.wYgNtQ/`; stable link `out/m2-vm-latest/` | Final rootfs SHA-256: ```text 81113e027b5e65c7885aac9d4b88541de92470aaffb1fefd9508de30b97f0cad ``` VM disk SHA-256, verified unchanged after the automated boot: ```text 00b48913a157de7a1107bf1bfff2c96db83dab48599fbe76be2ab433142d27d6 ``` The test directory retains `serial.log`, `command-line`, `test-environment.txt`, `test-inputs.sha256`, `disk-before.sha256`, and `artifacts.sha256`. The rootfs build retains its package inventory, exact package archives, build inputs, and hashes. Convenience links may change after future builds; this report names the retained successful directories. Rolling Void repositories are not a complete release snapshot, and local FDS packages remain unsigned development artifacts. ## What the ARM guest proved The VM booted the exported filesystem with its generated native init launcher. The test overlay preserves all 54 entries in the actual compiled service database and the original stage-2 script. Its hook runs the original stage 2 to completion, then runs the checker as a child. It neither substitutes a shell PID 1 nor compiles a different service graph. The checker is absent from the normal rootfs archive. The serial log records: ```text FDS_M2_PID1: /usr/bin/s6-svscan FDS_M2_FILESYSTEM: read-only root, writable tmpfs, root ownership and capabilities FDS_M2_BOOT: getty, eudev and Dasung supervised without a monitor FDS_M2_SERVICE: s6-rc start, echo, supervised restart, stop and start passed FDS_M2_PASS ``` The guest verified a read-only ext4 root, rejected a write under `/etc`, found writable tmpfs at `/run`, `/tmp`, `/var/tmp`, and `/var/log`, and checked hostname, root ownership, protected shadow permissions, and ping's restored capability. Eudev answered its control-channel readiness request; getty and Dasung were up. `test-echo` started down. The checker brought it up through s6-rc, waited for actual listener readiness, received an echo over its private Unix socket, restarted it under supervision and observed a new PID, stopped it, started it again, and stopped it before shutdown. Native s6 then completed power-off. The VM had no network, host filesystem share, physical USB/display access, or host disk passthrough. QEMU's disposable overlay preserved the backing disk. The interactive `make vm` workflow was also exercised: native PID 1, read-only mounts, active-service listing, Dasung status, manual echo-service control, and exit-to-power-off. Its transcript is `out/logs/m2-vm-shell.log`. Dasung returned valid status JSON with `connected: false`, as expected without a monitor. The explicit VM shell stops getty to take over the serial console; normal base accounts remain locked. ## Checks run These commands completed successfully during M2 validation: ```sh make bootstrap make smoke-test make check make rootfs PROFILE=cli make rootfs-test make init-test make dasung-test make vm ``` Rootfs acceptance checks cover package integrity, ARM glibc execution, static Dasung ELF properties, ownership/capability restoration, and rejection of wrong ABIs, forbidden packages, incomplete configuration, host executables, missing boot members, a shell init replacement, and unsupported profiles. Dasung regressions passed all six Rust unit tests, the serial simulator, package inspection, and its independent s6 logger/socket/restart/stop test on a read-only root. The final device-trigger guard changed only the init service definition; the rootfs and full ARM boot checks were repeated afterward. Shell/Python syntax, local documentation links, English project-owned text, recorded build/test source hashes, and unchanged upstream Void tracked files were checked. The source pin was preserved. ## Issues caught and fixed - The test kernel already mounts devtmpfs. Asking native init to mount `/dev` again caused a genuine kernel panic. The generated launcher now consumes the existing mount and leaves `/run` creation to s6-linux-init. - The generated init template contains FIFOs. Auditing/export now permits them only within its `run-image` subtree, and avoids reading them as ordinary files. - The upstream CA installer hides updater failures. One build lacked the bundle despite package configuration reporting success. Finalization now runs the updater explicitly, checks its exit status, verifies the resulting cache, and reports the failing command if any required finalization step fails. - VM image creation uses a UTF-8 locale for libarchive's Unicode paths. AHCI uses a QEMU snapshot overlay, with temporary files in the retained test directory. - Initial device triggering now propagates a failed subsystem trigger instead of continuing and reporting only the final command's result. The two essential internal s6-rc helpers remain until native shutdown's final cleanup; warnings that the ordinary service-stop phase leaves them running are expected. Known rootless `wall`, `write`, and `xbps-uchroot` ownership adjustments remain handled and verified by archive export, as documented for M1. ## Explicit skips and limits The M0 smoke script still skips its optional host-PATH `qemu-aarch64` execution because QEMU is installed inside the project-local container. The separate ARM userspace and complete ARM VM tests both passed. No Pi firmware, NVMe-to-cartridge root transition, production kernel/initramfs, physical Dasung picture or cold-power recovery, bay mapping, DATA safety, networking, or graphical profile was tested. There is no installable Pi image. The VM kernel is only a test fixture. Its log timestamps and 180-second test deadline are not boot or shutdown benchmarks. M3 and later implementation remain outside this change.