update docs

This commit is contained in:
2026-09-22 13:23:34 +08:00
parent 99bc3d15c5
commit 8a4788fca8
126 changed files with 7198 additions and 2425 deletions
+190
View File
@@ -0,0 +1,190 @@
# M2: native s6 init and the ARM development VM
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) · [Rootfs build](rootfs.md) · [Services](services.md)
M2 adds the system's real init path. The kernel enters the generated execline
launcher at `/sbin/init`; that immediately executes `s6-linux-init`, which prepares
`/run` and replaces itself with `s6-svscan`. The supervisor remains
PID 1. Bash is used for some stage-2 configuration tasks, as an ordinary child
process. No shell loop, systemd, runit, or BusyBox owns PID 1.
You can boot and inspect this system on the x86_64 workstation using a complete
ARM QEMU machine. This is a generic development VM, not a Raspberry Pi image.
It does not test Pi firmware, a cartridge boot path, or the physical monitor.
## Build and verify
First complete the [build-host setup](getting-started.md) and the
[rootfs prerequisites](rootfs.md#1-prepare-the-workstation). M2 additionally uses
the Arch `e2fsprogs` and `libarchive` packages to create an ext4 VM disk directly
from the tar, without mounting it or requiring root:
```sh
sudo pacman -S --needed e2fsprogs libarchive
make rootfs PROFILE=cli
make rootfs-test
make init-test
```
`make init-test` installs `qemu-system-aarch64` inside the project-local Void build
container when missing. Void packages its emulator with display, audio, and other
libraries, even though this test uses only a serial console. Those dependencies
stay in the build container; they do not become target packages or host services.
The test also downloads a signed generic ARM kernel, pinned by version and SHA-256
in `config/vm-test.conf`. Its AHCI, ext4, serial console and PCI drivers are built
in and devtmpfs is automounted by the kernel, so no test initramfs or replacement stage 0 is needed. The kernel package is
approximately 161 MB; only its boot files are extracted. Its module collection is
not installed into FDS. The separate [M4 boot test](boot.md) uses the actual
FDS Pi kernel and its initramfs-to-EROFS handoff.
The VM uses software CPU emulation, 1 GiB of RAM, two virtual CPUs, no network,
and no host USB, display, filesystem sharing, or block-device passthrough.
Its root filesystem is mounted read-only. QEMU discards any block changes in a
temporary disk overlay, preserving the generated test disk.
Successful output includes:
```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
PASS: M2 ARM boot, native s6 PID 1, service control, and orderly power-off
```
The test must finish within 180 seconds. This is a failure deadline, not a boot
performance target. A panic, failed assertion, missing marker, timeout, or missing
power-off message makes the command fail. Retained logs explain the result.
## Open a real booted ARM shell
After `make init-test` succeeds:
```sh
make vm
```
The same guest checks run, then the VM opens a temporary root shell on its serial
console. Try:
```sh
readlink /proc/1/exe
findmnt /
findmnt /run
s6-rc -l /run/s6-rc -a list
s6-svstat /run/service/dasungd
dasungd status
```
The rootfs's ordinary getty still requires authentication and root remains locked.
The VM hook stops getty before opening its shell so they do not compete for the
serial console; `s6-svstat` therefore reports getty down during that shell session.
The password-free shell exists only in the disposable VM test overlay, when
`make vm` explicitly requests it. The checker and shell hook do not ship in the
normal rootfs archive. Type `exit` to request orderly VM power-off. Ctrl+C stops
the emulator immediately if the guest is stuck; it does not shut down your host.
The shell's `/run`, `/tmp`, `/var/tmp`, and `/var/log` writes disappear at power-off.
Changes to `/etc` or `/usr` fail because SYSTEM is read-only. Rebuild the rootfs
and rerun `make init-test` after changing packages or init configuration; `make vm`
otherwise boots the previous successful test disk.
## Control the test service
The `test-echo` service is an actual Unix-socket echo server, supervised by s6.
It is included in the service database but excluded from the normal boot bundle.
In the VM shell:
```sh
s6-rc -l /run/s6-rc -u change test-echo
s6-svstat -o up,ready,pid /run/service/test-echo
printf 'hello from ARM\n' | s6-ipcclient /run/fds-test/echo.sock s6-ioconnect -t 3000
s6-svc -wR -T 5000 -r /run/service/test-echo
s6-rc -l /run/s6-rc -d change test-echo
```
Its socket is root-only, under `/run/fds-test` with mode 0700. The server sends an
actual readiness notification when it is listening. The automated test checks
the reply, confirms the PID changes on restart, stops the service, and starts it
again. No arbitrary boot delay substitutes for readiness.
## What starts at boot
| Component | Role and dependencies |
| --- | --- |
| Native stage 1 | Consume the kernel-provided `/dev`, mount `/run`; start s6 supervision and the catch-all logger |
| `runtime-fs` | Mount proc, sysfs, devpts and volatile temporary/log filesystems |
| `hostname` | Apply the image's `/etc/hostname` |
| `console` | Create volatile login accounting files after runtime mounts |
| `getty` | Offer a login on `/dev/console`, after console and hostname setup |
| `eudevd` | Process device events; readiness requires a successful control request |
| `udev-trigger` | Request initial subsystem and device events after eudev readiness |
| `dasungd-runtime`, `dasungd-log`, `dasungd` | Prepare private state/log paths and supervise the base monitor controller |
Console startup does not depend on eudev enumeration, a monitor connection, the
network, or a cartridge scan. No global `udevadm settle` is used. The eudev package
has no native s6 notification, so `s6-notifyoncheck` performs a bounded check of
its control channel. It signals readiness only after a successful response.
Cartridge discovery, the FDS CLI, desktop activation and on-demand Ethernet were
added after the original M2 milestone. The current image includes their real
implementations; see [Services](services.md), [Cartridges](cartridges.md) and
[Desktop](desktop.md). This page focuses on the native init mechanism and its
generic test fixture. SSH remains outside the default boot bundle.
## Files and build-time work
| Location | Meaning |
| --- | --- |
| `packages/fds-init/` | Init skeleton, runtime mount/accounting helpers, and package template |
| `s6/source/` | Complete current service graph, split between init, Dasung and cartridge/desktop packages |
| `/etc/s6-linux-init/current/` | Generated native init environment and stage scripts in the image |
| `/etc/s6-rc/compiled/` | Compiled service database, produced during image construction |
| `/run/service/`, `/run/s6-rc/` | Writable supervision and live service state |
| `/run/uncaught-logs/` | Catch-all service log, also copied to the console |
| `/run/log/dasungd/` | Rotating monitor-controller log |
| `out/m2-vm-latest/serial.log` | Last successful full ARM boot test |
| `out/m2-vm-latest/rootfs.ext4` | Disposable test disk; not a Pi or SYSTEM cartridge image |
`s6-linux-init-maker` and `s6-rc-compile` run during the rootfs build under ARM
emulation. Boot only reads their output. The VM test preserves that exact compiled
database. Its only init hook runs the original stage-2 script to completion, then
runs the checker as a child process. Normal `out/rootfs-aarch64.tar` contains
neither the checker nor the development-shell hook.
The s6-generated template contains FIFOs. Archive validation permits them only
inside its `run-image` directory; those templates are copied into writable `/run`
at boot. Before entering `/sbin/init`, the kernel or later stage 0 must provide
a mounted devtmpfs at `/dev`. Stage 1 deliberately does not mount it twice.
Services never place their live FIFOs or status files on read-only SYSTEM.
M10 adds cartridge-aware preparation before s6-rc stops the remaining services.
Its native shutdown hook stays alive if DATA cannot be verified and unmounted.
`s6-linux-init-shutdownd` still performs final process cleanup and the kernel
power action. See [Shutdown and reboot](power.md) for usage, blocked-request
recovery, and the current M10 acceptance status.
## Troubleshooting
- **`make vm` asks you to run `make init-test`:** no successful VM disk has been
published yet. Build the rootfs, then run the test.
- **Kernel pin/hash failure:** do not substitute an unverified kernel. The exact
configured package must remain available or be deliberately updated and retested.
- **Disk creation reports missing tar support:** use an `e2fsprogs` build with
libarchive support and install `libarchive`. No loop mount or sudo fallback is used.
- **No guest success marker:** read `out/logs/init-checks.log` and the printed
`out/m2-vm.*` directory's `serial.log`. Failed test workspaces are retained.
- **Root login is denied on the ordinary getty:** this is expected with the locked
base accounts. Use the explicit development VM shell, not a default password.
- **Dasung says disconnected:** the VM intentionally has no physical monitor.
Use [Dasung integration](dasung.md) for its eventual hardware validation.
Upstream references: [s6-linux-init](https://skarnet.org/software/s6-linux-init/overview.html),
[init generation](https://skarnet.org/software/s6-linux-init/s6-linux-init-maker.html),
[s6-rc initialization](https://skarnet.org/software/s6-rc/s6-rc-init.html), and
[ext4 creation](https://man7.org/linux/man-pages/man8/mke2fs.8.html).