1
Developer M9 Validation
Felis edited this page 2026-09-24 13:51:28 +08:00
Table of Contents
include_toc
include_toc
true

M9 media-tool validation

Development reference and historical context. For current operating instructions, use the user manual. Acceptance applies only to the source and artifacts identified in each record.

Media-tool usage · Implementation ledger

M9 software acceptance passed on 2026-09-21. Both packaged profiles, ARM image construction, confirmed virtual USB writes, SYSTEM creation/write/boot, and the earlier-milestone regression checks passed. All devices in these tests are disposable regular files exposed only to isolated ARM virtual machines. Physical Pi, media-controller and display tests remain deferred.

Verified behavior

make media-image-test created SYSTEM, DATA, PROGRAM and ENVIRONMENT images with the actual static ARM fds-burn. Independent sfdisk, Python SHA-256, fsck.erofs and e2fsck checked the outputs. Invalid GPT extents, extra partitions, internal partition labels and inconsistent backups were rejected even when the fixture recomputed valid CRCs. Existing output files and recursive source/output layouts were refused. Evidence: out/m9-images.zqojr8hc/.

make media-test verified these cases through the ordinary FDS user's CLI:

  • Inspection reports the bay's capacity, logical sector size, model, serial and kernel insertion identity. The running SYSTEM and mounted media refuse writes.
  • Preview and cancellation leave the target unchanged. A confirmation is bound to its operation, bay, insertion and image hash; a wrong phrase cannot proceed.
  • Changing source bytes after preview is detected before writing. Replacing the device in the same bay invalidates the old confirmation and leaves the new insertion untouched.
  • A PROGRAM image is written, flushed, read back and marked SAFE. Its backup GPT moves to the end of a larger device. SAFE survives a daemon restart; a new insertion mounts normally and supports explicit unprivileged execution.
  • PROGRAM, DATA and ENVIRONMENT images are created on the running ARM machine. DATA permits ordinary-user writes, retains data across removal/reinsertion and passes independent host e2fsck after safe eject.
  • Cartridge status remains usable while a separate write worker is active. Source DATA cannot be ejected while the worker holds its image, including across the worker's private mount namespace.
  • Killing the cartridge daemon during an awaiting-confirmation operation leaves a failed record on restart. It never becomes SAFE or automatically remounts.
  • A valid EROFS/GPT containing an approximately 60 KiB malformed manifest is rejected before writing. Its diagnostic stays bounded and printable, the daemon retains the same process ID, and subsequent status requests work.

The final virtual USB evidence is out/m9-vm.cdlj6_r7/. The crash case uses native s6 supervision controls; see the upstream s6-svc interface.

SYSTEM creation through boot

tests/integration/m9-system.py mounts a complete prepared SYSTEM after boot, then runs the real ARM filesystem/image builder against that read-only tree. The ordinary user previews and confirms a write to another virtual cartridge. After device flush and readback verification, a fresh VM boots the written disk through the actual Pi kernel, stage0 and native s6 to the ordinary FDS prompt. The test verifies UID 1000, read-only SYSTEM behavior, Dasung, the preserved root:utmp terminal-helper ownership, and packaged Terminus caches without a new user cache. Final evidence: out/m9-system.ej8oz_av/.

This demonstrates creation and boot of a usable SYSTEM on ARM, not just a valid partition header. It does not emulate Raspberry Pi firmware, physical NVMe/RP1, USB power behavior or Dasung video output.

Implementation and dependencies

fds-burn is static-musl Rust. fds-inspect and fds-eject are static aliases of fds; the daemon starts a bounded worker for each media operation. The worker opens the exact disk with exclusive access, checks live identity again before writing, validates the image's filesystem and manifest, and verifies actual readback after flushing and invalidating the device cache. A root-owned record prevents interrupted operations from acquiring a false SAFE status after restart.

Protection checks cover mounts, swap, kernel holders and the three reserved internal partition names. Existing primary and backup GPTs are also inspected directly, so protection does not depend solely on partition uevent timing. Native 4 KiB logical-sector disks are explicitly unsupported by this writer.

The eight new locked Rust dependency entries are explained in the media guide. Filesystem tools and GNU coreutils already belong to the base image; their package dependencies are now declared explicitly. No init replacement, background network service, new native library dependency of an FDS executable, or boot-time cache builder was introduced. Void's tracked source files remain unchanged.

Findings resolved during validation

A private checker device node initially failed to open because /run is mounted with nodev. The final code creates an inaccessible private directory on devtmpfs, opens its read-only device inode, and immediately unlinks it. The filesystem checker receives that handle and runs as UID 1000; global /dev/loopN permissions are unchanged. Image mounts exist only in the worker's private mount namespace and disappear when it exits.

The namespace review also identified the need to block eject until the worker exits: unmounting only the daemon's namespace could otherwise report a source DATA cartridge SAFE while the worker retained its mount. The VM test now covers that exact case.

Malformed TOML can include most of its source text in an error message. Worker diagnostics are now limited to 2048 UTF-8 bytes plus a truncation suffix, with control characters replaced by spaces. This prevents cartridge contents from overflowing the daemon's status protocol or injecting terminal controls.

The sustained-write regression initially failed because kernel I/O diagnostics interrupted CLI JSON in the middle of a serial line during deliberate removal. The test now executes each command once, saves its stdout, and transfers it with base64 and a SHA-256 checksum. A damaged transfer is retried without rerunning the command. Kernel output remains in the serial log and a separate diagnostic capture. The corrected test passed in out/m7-vm.t5d5fd_4/, including independent ext4 checks and the exact 64 MiB write payload.

Packaged artifacts and regressions

The rebuilt CLI rootfs contains 180 packages and 1393 AArch64 ELF files. All FDS executables remain static-musl. Package archives, input hashes, ownership and capability records are retained beside the rootfs.

Artifact Location SHA-256
CLI rootfs, 498616320 bytes out/rootfs-build.gs9ZTA/rootfs-aarch64.tar 4abbc4e4e1823ef78acde2645d915160aa8e1c0f8b3173e8bc73f8d6b923d502
CLI SYSTEM out/system-build.a74aryls/system.img b7a71dad45c259c92ec5a5ac72b87356132f642df5c33d74d20e2e6ca564dee6
FAT32 boot partition out/boot-build.oapz3fuf/boot.fat ac3ee864c6293dd30a640fbbc0514b08acb92cda2d25d05128d234229308ac8f

The earlier-milestone regression run passed tooling, formatting/build checks, rootfs rejection checks, native init, the stage0 boot matrix, console and cartridge tests. Its logs are out/logs/m9-final-*.log; the corrected DATA run is out/logs/m9-data-retry3.log. The final diagnostic change was then rebuilt into the CLI archive and passed the complete media write/boot and archive checks in out/logs/m9-hardened-*.log.

Native init evidence is out/m2-vm.ijzHZC/, the boot matrix is out/m4-vm.sxNb8D/, console checks are out/m5-vm.6suv4qjj/, and cartridge checks are out/m6-vm.o5sjhatc/. Kernel-to-console was 1854.858231 ms in this VM run, compared with M8's 1854.724813 ms: +0.133418 ms. The actual static ARM comparison passed without an exception; see out/logs/m9-boot-comparison.log. This is below the 100 ms review threshold and is not a Pi performance result.

The development rootfs contains 191 packages and 1405 AArch64 ELF files. make rootfs-test, make desktop-test and development SYSTEM construction passed, followed by the final tooling and formatting/build checks. Evidence is out/m8-vm.498wsxsv/ and out/logs/m9-hardened-*.log. The screenshot was inspected and retains the grayscale Terminus terminal. First desktop activation was 1111.300247 ms in this VM; physical display timing remains unmeasured.

Artifact Location SHA-256
Development rootfs, 501882880 bytes out/rootfs-build.sQCq5V/rootfs-aarch64.tar 87e9a35829432f07e4a4bfa4c69eabd5ba0f30a25eadfed05ea9393bd1fb2afa
Development SYSTEM out/system-build.l5e3j1pg/system.img bcb8dfa7ec6e3207080f5c68074cc0c27d1bea63a0b51561c8ec628691e03611

The generic rootfs convenience links were restored to the CLI build after the development checks. out/rootfs-cli.tar and out/rootfs-development.tar retain explicit pointers to these two verified archives.

Deferred and remaining acceptance

Physical media throughput, flash-controller write-cache behavior, USB brownouts, power-loss durability and Pi boot remain deferred. No throughput or hardware latency claim is made. Broader I/O failure injection and twelve-device stress belong to M11; ordered power-off and persistent DATA error handling belong to M10; signing, independent recovery and complete release inputs belong to M12.