277 lines
14 KiB
Markdown
277 lines
14 KiB
Markdown
# Internal storage and machine settings
|
|
|
|
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 images](boot.md) · [Recovery](recovery.md)
|
|
|
|
The internal NVMe supplies the Pi's firmware boot files, independent recovery,
|
|
and machine settings. SYSTEM remains a separate removable cartridge. Ordinary
|
|
user files belong on DATA, not internal storage. No physical disk is written by
|
|
any build command below; Pi/NVMe boot and power-loss tests remain deferred.
|
|
|
|
## Build the complete disk image
|
|
|
|
Build the component images first, in this order:
|
|
|
|
```sh
|
|
make kernel
|
|
make initramfs
|
|
make boot-volume BOOT_MODE=production
|
|
make recovery
|
|
make internal-image
|
|
```
|
|
|
|
The result is `out/fds-internal.img`, a **complete GPT disk image**. Its versioned
|
|
build directory contains `layout.json`, source payload checksums, and readback
|
|
verification. This differs from `out/fds-boot.img` (a raw FAT partition) and
|
|
`out/fds-recovery.img` (a raw EROFS partition).
|
|
|
|
| Partition | Format | Default allocation | Purpose |
|
|
| --- | --- | --- | --- |
|
|
| 1: FDS_BOOT | FAT32, EFI system type | 512 MiB | Pi firmware, kernel, DTBs, configuration and initramfs |
|
|
| 2: FDS_RECOVERY | EROFS | 1 GiB | Complete independent maintenance system |
|
|
| 3: FDS_INTERNAL | ext4 | 256 MiB | Machine settings and explicitly saved diagnostics |
|
|
|
|
The builder pads the recovery partition without changing its EROFS contents,
|
|
aligns partitions to 1 MiB, writes both GPT copies, verifies each payload, and
|
|
checks the table independently with `sfdisk`. Internal ext4 is fully initialized
|
|
before deployment. It contains root-owned `config/` and private `diagnostics/`.
|
|
Unused capacity beyond this initial layout is not automatically expanded.
|
|
|
|
For a 2 GiB recovery allocation or larger settings partition:
|
|
|
|
```sh
|
|
./image/build-internal --recovery-mib 2048 --internal-mib 512
|
|
```
|
|
|
|
Output directories supplied with `--output-directory` must already exist and be
|
|
empty. The builder accepts ordinary image files, never a host block-device
|
|
output. Final hardware provisioning and identity checks belong to the physical
|
|
acceptance procedure; do not confuse a partition payload with the complete disk.
|
|
|
|
## Install the internal disk when hardware is available
|
|
|
|
This is a **deferred physical procedure**. The image and virtual NVMe workflow
|
|
are software-tested; writing and booting the user's actual NVMe still require
|
|
the hardware. Installation erases the selected disk, including any existing
|
|
machine settings. Retain backups before replacing an existing installation.
|
|
|
|
Use an NVMe enclosure or another Linux machine that can access the target drive
|
|
while the Pi is off. First verify a downloaded release using a separately trusted
|
|
public key as described in [Release signatures](releases.md). Its complete disk
|
|
is named `fds-internal-0.1.0.img`; a local build uses `out/fds-internal.img`.
|
|
Use the complete disk image, not the separate BOOT or RECOVERY payload.
|
|
|
|
List disks before and after connecting the intended drive:
|
|
|
|
```sh
|
|
lsblk -d -o NAME,PATH,MODEL,SERIAL,SIZE,TRAN,LOG-SEC
|
|
ls -l /dev/disk/by-id/
|
|
```
|
|
|
|
Match the physical model, serial and capacity. Choose a persistent **whole-disk**
|
|
`/dev/disk/by-id/` path without a `-partN` suffix; never guess a `/dev/sdX` name.
|
|
Unmount every target partition and disable any swap on it. Do not select the
|
|
workstation's system disk. The current images require 512-byte logical sectors
|
|
and a disk at least as large as the image; 4 KiB logical-sector media is not
|
|
supported by this layout.
|
|
|
|
Open Bash (`bash`), replace both paths below, then run the block. It checks the
|
|
selected disk again, requires a typed confirmation, writes and reads back the
|
|
complete image, and relocates the backup GPT when the disk is larger than the
|
|
image. It does not expand any partition or filesystem.
|
|
|
|
```bash
|
|
(
|
|
set -euo pipefail
|
|
fds_image="$PWD/out/fds-internal.img"
|
|
fds_disk=/dev/disk/by-id/REPLACE_WITH_THE_TARGET_DISK
|
|
[[ -f "$fds_image" && -s "$fds_image" && -b "$fds_disk" ]]
|
|
[[ $(lsblk -dnro TYPE "$fds_disk") == disk ]]
|
|
[[ $(sudo blockdev --getss "$fds_disk") == 512 ]]
|
|
fds_bytes=$(stat -Lc %s "$fds_image")
|
|
(( $(sudo blockdev --getsize64 "$fds_disk") >= fds_bytes ))
|
|
lsblk -p -o NAME,TYPE,MODEL,SERIAL,SIZE,MOUNTPOINTS "$fds_disk"
|
|
if lsblk -nrpo MOUNTPOINTS "$fds_disk" | grep '[^[:space:]]' >/dev/null; then
|
|
echo 'Target has mounted filesystems or swap; stop and release them first.' >&2
|
|
exit 1
|
|
fi
|
|
if lsblk -nrpo TYPE "$fds_disk" | grep -Ev '^(disk|part)$' >/dev/null; then
|
|
echo 'Target has device-mapper or other active descendants; stop.' >&2
|
|
exit 1
|
|
fi
|
|
read -r -p "Type ERASE $fds_disk to erase this disk: " fds_confirmation
|
|
[[ "$fds_confirmation" == "ERASE $fds_disk" ]]
|
|
sudo dd if="$fds_image" of="$fds_disk" bs=4M conv=fsync status=progress
|
|
sudo blockdev --flushbufs "$fds_disk"
|
|
sudo cmp -n "$fds_bytes" "$fds_image" "$fds_disk"
|
|
sudo sfdisk --lock=yes --relocate gpt-bak-std "$fds_disk"
|
|
sudo sfdisk --verify "$fds_disk"
|
|
sudo blockdev --flushbufs "$fds_disk"
|
|
)
|
|
```
|
|
|
|
Every command must succeed. The byte comparison occurs before relocating GPT,
|
|
because relocation intentionally changes disk-table headers. GNU `dd`'s `fsync`
|
|
flushes output before it returns; `sfdisk`'s `gpt-bak-std` moves the backup header
|
|
to the end of the target. See the [GNU dd manual](https://www.gnu.org/s/coreutils/manual/html_node/dd-invocation.html)
|
|
and [sfdisk manual](https://man7.org/linux/man-pages/man8/sfdisk.8.html).
|
|
These commands use the already documented coreutils, diffutils and util-linux
|
|
host tools; they do not add a target daemon.
|
|
|
|
Confirm that the disk shows `FDS_BOOT`, `FDS_RECOVERY` and `FDS_INTERNAL` in that
|
|
order using `lsblk -o NAME,PARTLABEL,FSTYPE,MOUNTPOINTS`. Unmount anything the
|
|
desktop automatically mounted, then safely disconnect the enclosure and install
|
|
the NVMe in the powered-off Pi. Follow [EEPROM preparation](eeprom.md) for the
|
|
separately reviewed NVMe boot settings.
|
|
|
|
Prepare the **first SYSTEM cartridge on the workstation** using the same guarded
|
|
write block, with its two path assignments changed to the chosen SYSTEM image
|
|
and a different, empty USB cartridge disk. For a local build select
|
|
`out/fds-system-cli.img` or `out/fds-system-development.img`; release files are
|
|
`fds-system-cli-0.1.0.img` and `fds-system-development-0.1.0.img`. They are
|
|
alternative complete GPT images, each containing one `FDS_SYSTEM` partition.
|
|
Readback and backup-GPT relocation apply to this disk too. Check its partition
|
|
label, safely disconnect it, and insert exactly one SYSTEM cartridge in the Pi
|
|
before normal boot. Subsequent cartridge creation and updates can use FDS's
|
|
confirmed [media workflow](media-tools.md) after bay calibration.
|
|
|
|
On the first physical boot, check `fds info`, `fds machine status`, and
|
|
`fds bays`. The supplied bay map is empty until calibration. Follow
|
|
[physical acceptance](stress-testing.md) to measure ports, test the Dasung
|
|
display, and record actual boot/shutdown behavior before relying on the machine.
|
|
|
|
## Configure the machine before building
|
|
|
|
Copy `config/machine/` to your own directory. It contains three files:
|
|
|
|
- `machine.toml`: `format = 1` and a short human-readable `name`.
|
|
- `bays.toml`: the measured controller/port map described in
|
|
[Cartridges and bay calibration](cartridges.md).
|
|
- `hardware-catalog.toml`: optional USB identification names using the same schema
|
|
as the base catalog. Entries are data and cannot run commands.
|
|
|
|
The supplied bay map is deliberately empty because the physical wiring has not
|
|
been measured. Do not invent Pi USB paths. USB 2 and USB 3 companion ports need
|
|
explicit aliases for the same bay.
|
|
|
|
```sh
|
|
cp -a config/machine out/my-machine
|
|
# Edit the three files in out/my-machine using your editor.
|
|
make internal-image MACHINE_CONFIG=out/my-machine
|
|
```
|
|
|
|
The builder compiles a native host copy of `fds` and uses the same strict parser
|
|
as the target system. You can also validate or pack settings yourself:
|
|
|
|
```sh
|
|
cargo build --locked --offline --release --target x86_64-unknown-linux-gnu -p fds-cli
|
|
./target/x86_64-unknown-linux-gnu/release/fds machine validate out/my-machine
|
|
./target/x86_64-unknown-linux-gnu/release/fds machine pack out/my-machine out/my-machine.json
|
|
```
|
|
|
|
The three source files become one atomic `config/machine.json` document on
|
|
FDS_INTERNAL. Names, lengths, bay aliases, catalog fields and unknown keys are
|
|
validated before use. Do not put passwords or private keys in this configuration:
|
|
its active snapshot is readable by the local FDS user.
|
|
|
|
## What happens at boot
|
|
|
|
The `machine-config` native s6 oneshot precedes `cartridged`. It does **not**
|
|
precede the console or Dasung controller. It accepts exactly one non-removable
|
|
NVMe disk with the three named partitions in the order above. USB lookalikes are
|
|
ignored; multiple eligible NVMe disks are rejected instead of choosing by name.
|
|
|
|
The settings partition must be clean ext4 with the expected label. Loading uses
|
|
`ro,noload,nosuid,nodev,noexec` in a private mount namespace, validates the entire
|
|
settings bundle, copies it into `/run/fds/machine/`, and unmounts. No filesystem
|
|
repair, journal replay, cache compilation or persistent write occurs during
|
|
normal boot. The cartridge service uses that snapshot throughout this boot,
|
|
including after a service restart.
|
|
|
|
If internal storage is missing, unclean, invalid or ambiguous, the service records
|
|
an explanation and uses the immutable image's `/etc/fds/` defaults. The console
|
|
still opens. Check the actual source before treating bays as calibrated:
|
|
|
|
```sh
|
|
fds machine status
|
|
fds --json machine status
|
|
fds machine export /tmp/current-machine
|
|
```
|
|
|
|
Export creates a new directory with the three editable source files. It never
|
|
overwrites an existing directory. The status source is `internal_nvme` or
|
|
`image_defaults`. A temporarily unavailable NVMe is not adopted later in the same
|
|
boot; resolve the issue and reboot to load its settings. This prevents changing
|
|
bay identities underneath active cartridge operations.
|
|
|
|
## Update settings from recovery
|
|
|
|
Bring the edited source directory on a DATA cartridge. At the local root
|
|
`RECOVERY#` console, identify its bay with `fds bays`; healthy DATA is mounted
|
|
read-only under `/run/fds/media/NN`. For example, if it is in BAY 02:
|
|
|
|
```sh
|
|
fds machine validate /run/fds/media/02/my-machine
|
|
fds machine install /run/fds/media/02/my-machine
|
|
fds reboot
|
|
```
|
|
|
|
Installation is restricted to root in the recovery image. It writes the complete
|
|
validated bundle atomically, saves the old bytes as `config/previous.json`,
|
|
flushes and unmounts the internal filesystem, and reports success only after
|
|
those steps. A reboot activates the new settings. The currently running bay map
|
|
is unchanged, so active media does not move to a different bay mid-operation.
|
|
Keep a copy of your previous source directory on DATA or the build host to
|
|
reinstall it if the new calibration is wrong.
|
|
|
|
An invalid existing JSON document can be replaced this way. Wrong ownership,
|
|
symlinks, an unclean filesystem or a damaged directory require offline filesystem
|
|
maintenance first. Recovery does not automatically repair internal NVMe; its
|
|
`fds recovery repair` command is deliberately limited to DATA cartridges.
|
|
|
|
## Save and retrieve diagnostics
|
|
|
|
Logs and boot records stay in RAM by default. Root may explicitly save a file
|
|
of up to 16 MiB. Saved names cannot contain paths, and existing names are refused.
|
|
These operations mount internal ext4 only for the operation and then unmount it.
|
|
For example, from recovery:
|
|
|
|
```sh
|
|
fds --json boot-profile >/tmp/boot.json
|
|
fds machine store boot-first.json /tmp/boot.json
|
|
fds --json bays >/tmp/cartridge-inventory.json
|
|
fds machine store cartridges-first.json /tmp/cartridge-inventory.json
|
|
bash /usr/share/fds/capture-hardware /tmp/hardware-capture
|
|
tar -C /tmp -czf /tmp/hardware-capture.tar.gz hardware-capture
|
|
fds machine store hardware-first.tar.gz /tmp/hardware-capture.tar.gz
|
|
fds machine fetch boot-first.json /tmp/retrieved-boot.json
|
|
```
|
|
|
|
Use distinct names for subsequent sessions. A failed flush or unmount is an
|
|
error, not a successful save. These are machine diagnostics; do not use this
|
|
facility as ordinary user storage. The saved cartridge inventory is a persistent
|
|
diagnostic snapshot, including metadata already inspected during this boot.
|
|
Retrieve it with `fds machine fetch cartridges-first.json /tmp/saved-inventory.json`.
|
|
The daemon's live metadata cache remains volatile and is rebuilt from currently
|
|
attached devices; saved snapshots are never used to authorize media actions.
|
|
Together, explicit boot reports, inventory snapshots and hardware captures provide
|
|
the boot history, cached metadata and diagnostics assigned to FDS_INTERNAL in
|
|
master-plan section 12, without adding internal writes to startup or shutdown.
|
|
|
|
## Dependencies and validation
|
|
|
|
No new target package or Rust crate is required. The host image-tool prefix adds
|
|
`e2fsprogs` for ext4 creation, inspection and validation; it already supplies FAT
|
|
and EROFS tools. A private unprivileged user namespace gives created files root
|
|
ownership without requiring a root build session.
|
|
|
|
`make internal-test` exercises the actual packaged runtime in ARM VMs with
|
|
virtual NVMe. Acceptance evidence belongs in [M12 validation](m12-validation.md);
|
|
a passing VM does not verify the Pi EEPROM, PCIe path or physical flash durability.
|
|
|
|
The Linux [ext4 mount documentation](https://www.kernel.org/doc/html/latest/admin-guide/ext4.html)
|
|
explains why read-only loading also disables journal replay. Filesystem creation
|
|
options follow the upstream [mke2fs manual](https://man7.org/linux/man-pages/man8/mke2fs.8.html).
|