Files
fds-os/docs/cartridges.md
T
2026-09-21 22:29:23 +08:00

198 lines
8.8 KiB
Markdown

# Using cartridges and configuring bays
[Documentation index](README.md) · [Boot images](boot.md) · [Services](services.md)
M6 adds the base `fds-cartridged` service, twelve bay states, USB hardware
recognition, strict metadata inspection, read-only storage mounts and eject.
Its [ARM VM acceptance checks passed](m6-validation.md). [Writable DATA integration](data.md) has passed M7 software acceptance;
[desktop/program activation](desktop.md) passed M8 and [media creation tools](media-tools.md)
passed M9 software acceptance. [M10 ordered shutdown](power.md) has passed software acceptance.
## Inspect the current machine
At the FDS console:
```sh
fds bays
fds bay 3
fds cartridge 3
fds --json bays
```
The ordinary `fds` user can use these commands. `bays` shows all twelve bays,
including empty and unconfigured ones. `cartridge` includes validated metadata
and the current mount path. `--json` exposes a versioned machine-readable report.
`fds rescan` explicitly refreshes inventory; normal insert/remove events trigger
refresh automatically. The console does not wait for the inventory scan.
| State | Meaning and next action |
| --- | --- |
| UNCONFIGURED | No measured controller/port mapping exists for this bay |
| EMPTY | Configured bay with no detected USB device |
| HARDWARE | USB device without storage; catalog name or VID/PID is shown |
| UNRECOGNIZED STORAGE | Storage exists but no named FDS partition is available |
| MOUNTED READ ONLY | Manifest validated; filesystem available at the displayed path |
| MOUNTED READ WRITE | Active DATA is writable at `/data`; eject before removal |
| PROTECTED | The active SYSTEM root; eject is refused |
| AMBIGUOUS | Multiple devices or named partitions match; nothing is selected arbitrarily |
| ERROR | Inspection, mounting or metadata validation failed; read the diagnostic |
| SAFE | The requested unmount succeeded; remove the cartridge |
An unrecognized storage state may briefly appear while the kernel discovers its
partitions. It is not permission to remove a device being used elsewhere.
Errors and ambiguous media never produce SAFE. An additional mount of the same filesystem also blocks managed eject until
it is unmounted.
## Calibrate the physical bay map
The shipped `/etc/fds/bays.toml` is deliberately empty: no Pi bay wiring has yet
been measured. This produces UNCONFIGURED states, not invented assignments.
1. On the assembled machine, insert one known USB device into one physical bay.
2. Run `fds topology`. This diagnostic explicitly shows controller/port paths;
ordinary bay commands omit those implementation details.
3. Record that path and repeat for each bay. Test both USB 2 and USB 3 devices,
because their companion root hubs can have different logical port paths.
4. Put the measured map in your machine configuration directory and build the
[internal image](internal-storage.md), or use `fds machine install DIRECTORY`
from recovery and reboot. Machine settings persist independently of SYSTEM.
The packaged `/etc/fds/` files are fallback defaults for missing/invalid internal
storage; `fds machine status` shows which source is active.
5. Verify all twelve devices together, in different insertion orders and after
reboot. That physical acceptance remains deferred.
A **syntax example only**, using identities that must be replaced by observations:
```toml
[front]
hub = "platform/example-controller:usb2/1"
[front.ports]
1 = 1
2 = 2
3 = 3
4 = 4
5 = 5
6 = 6
[front_superspeed]
hub = "platform/example-controller:usb3/1"
[front_superspeed.ports]
1 = 1
2 = 2
3 = 3
4 = 4
5 = 5
6 = 6
```
`hub` names the controller and parent port chain. Its numbered `ports` map the
next downstream port to a bay number from 1 through 12. Additional groups can
map the rear hub. Explicit USB 2/3 aliases may refer to the same bay; duplicate
ports, overlapping parent/child mappings, and duplicate bays within one hub are
rejected. If two devices simultaneously match aliases for one bay, it becomes
AMBIGUOUS. A hub inserted into a cartridge bay with multiple downstream devices
also needs an explicit future composite-device policy; M6 refuses to guess.
The identity excludes the Linux USB bus number and `/dev/sdX` enumeration order.
It includes the controller's sysfs path, USB protocol generation and port chain.
Moving a hub to another controller or changing wiring requires recalibration.
## Storage formats and metadata
| Class | GPT partition name | Filesystem and current behavior |
| --- | --- | --- |
| SYSTEM | FDS_SYSTEM | EROFS; current root is protected, additional media read-only |
| DATA | FDS_DATA | ext4; inspected read-only first, then activated at `/data` under the M7 policy |
| PROGRAM | FDS_METADATA + FDS_PAYLOAD02… | Metadata and xz bundles in `1+m` EROFS partitions; see [software format](software-format.md) |
| Legacy PROGRAM | FDS_PROGRAM | Existing single EROFS application tree; still readable |
| ENVIRONMENT | FDS_ENVIRONMENT | EROFS; declarative selection of a trusted built-in profile |
| UTILITY | FDS_UTILITY | EROFS; inspection only, no automatic actions |
| HARDWARE | None required | VID/PID, optional serial, device/interface class and bay |
Storage contains `/FDS/CARTRIDGE.TOML`. For example:
```toml
format = 1
[cartridge]
id = "fds.windowmaker"
name = "WINDOW SYSTEM"
class = "environment"
version = "0.1"
[media]
writable = false
[activation]
profile = "windowmaker"
```
The manifest class must match the GPT partition name. SYSTEM, PROGRAM and
ENVIRONMENT are read-only; DATA declares writable media during the initial read-only inspection. Unknown keys, root commands, unsafe identifiers, control
characters, symlink metadata and nonregular metadata files are rejected.
The parser accepts at most 64 KiB. No `/FDS/autorun.sh` is run.
Inspect a standalone manifest with `fds inspect /path/to/CARTRIDGE.TOML`.
For mounted storage, `fds cartridge N` reports the daemon's validated copy.
Media is inspected in a root-only staging directory, then published at
`/run/fds/media/NN` after validation, with nodev, nosuid and noexec. Device
major/minor and kernel disk sequence are checked before mounting an opened
block-device descriptor, preventing stale enumeration names from selecting a
replacement disk.
## Eject a mounted cartridge
```sh
fds eject 2
```
Leave any shell working directory inside that cartridge and close files first.
SAFE is emitted only after an ordinary unmount succeeds. Busy mounts return an
error; forced or lazy unmount is never used to declare safe removal. Once SAFE,
the daemon keeps that insertion unmounted until removal and reinsertion, including
across a service restart. Its root-owned volatile marker is tied to the kernel
disk sequence, so a replacement disk is not mistaken for ejected media.
The active SYSTEM cannot be ejected. Swap SYSTEM only after shutting down.
Pulling a mounted cartridge without eject is surprise removal. The daemon clears
its inventory and detaches a vanished read-only mount where needed; that cleanup
is not a successful eject. [M7 DATA handling](data.md) extends ejection to writable DATA, consumer
tracking and syncfs. The temporary `/home/fds` remains usable without DATA and is lost at
power-off.
## Hardware recognition
Edit `packages/fds-cartridged/files/hardware-catalog.toml`, then rebuild SYSTEM.
Entries name devices, never executable actions:
```toml
[[device]]
name = "MY SERIAL ADAPTER"
vendor = "1234"
product = "5678"
# Optional exact restrictions:
serial = "UNIT-1"
class = "02"
```
Replace these example IDs with observed values. Device or interface class may
match `class`. Multiple matching catalog entries produce an error; unknown
hardware remains identified by VID/PID without being mistaken for empty media.
Dasung control remains in the independent base monitor service.
## Implementation and diagnostics
The daemon subscribes to kernel USB/block events before taking its first snapshot.
A receive-buffer overflow causes a fresh snapshot. IPC uses a bounded Unix socket
at `/run/fds/control.sock`, mode 0660 and group `fds`, with peer UID checks for
root and the ordinary FDS user. Slow clients have individual deadlines and do not
block other clients. No asynchronous runtime or new external Rust dependency is
introduced: the existing std, libc, serde, serde_json and toml components suffice.
Root diagnostics: `s6-svstat /run/service/cartridged` and
`cat /run/log/cartridged/current`. Logs are bounded and volatile. Restart through
`s6-rc -l /run/s6-rc -d change cartridged`, then the corresponding `-u` command.
Close users of cartridge mounts first; restart cleanup refuses busy leftovers.
On the build host, `make cartridge-test` runs fixtures and full virtual USB tests.
These exercise actual kernel events and mounts but cannot establish physical
wiring, USB power stability, or Pi port behavior. M11 expands to twelve-device
stress, and physical calibration must be recorded separately.