Files
fds-os/docs/workstation.md
T

275 lines
11 KiB
Markdown

# Build software cartridges and use the emulator
This walkthrough runs on a Linux workstation. It builds applications from Void
source packages, creates a complete cartridge image, runs it in FDS, and explains
how to write it to USB. Commands run from the FDS checkout root unless marked
as guest commands.
## Prepare the tools
The native tools are `fds-cartridge` and `fds-emulator`. On Arch Linux, build
and install their `fds-tools` package from the checkout:
```sh
sudo pacman -S --needed base-devel rustup python git
make workstation
make workstation-install
```
`make workstation` produces `out/workstation/fds-tools-VERSION-1-ARCH.pkg.tar.zst`
with both commands, documentation, examples and license notices. It does not
install packages. `make workstation-install` builds that package and invokes
`sudo pacman -U` to install it into `/usr/bin`. Pacman tracks upgrades and removal;
remove it later with `sudo pacman -R fds-tools`. The exact latest package path is
recorded in `out/workstation/package-path.txt`.
The package requires glibc, GCC runtime libraries and xz. EROFS, bubblewrap and
QEMU are optional package dependencies because the tools can use the project-local
Void runners instead. For all features using native Arch utilities, install:
```sh
sudo pacman -S --needed bubblewrap erofs-utils qemu-system-aarch64 qemu-img
```
On other Linux distributions, install Rust, a C linker, Make, Python 3 and Git,
then build the same native commands without Arch packaging:
```sh
make workstation-binaries
export PATH="$PWD/out/workstation:$PATH"
```
Python and Git resolve the build version; neither is needed to run the installed
Rust commands. The repository selects its pinned Rust toolchain. XBPS and a
prepared Void checkout are still separate source-build prerequisites.
| Prerequisite | Used for |
| --- | --- |
| Prepared writable Void `void-packages` checkout and native XBPS utilities | Build source packages and install their AArch64 runtime dependencies |
| `mkfs.erofs`, `fsck.erofs` | Create and inspect read-only payload filesystems |
| `bwrap` | Inspect filesystems and install package trees in a user namespace |
| `xz` | Read older software cartridges and package/source archives |
| `qemu-system-aarch64`, `qemu-img` | Run the emulator and create writable DATA overlays |
Install tools through your Linux distribution's package manager. Enable
unprivileged user namespaces for bubblewrap. Software builds run as an ordinary
user and execute trusted Void build templates on the workstation. Run source builds sequentially when they
share the same Void checkout.
Prepare a Void source checkout using its [upstream instructions](https://github.com/void-linux/void-packages/blob/02a3cbc132c3c4a3a9d59e9b98f517af5dd11cd1/README.md).
Complete `xbps-src binary-bootstrap` for the workstation architecture before
using it. Keep this writable checkout outside Git submodules. The workstation itself need not run Void. Select that checkout with
`--void-packages /path/to/void-packages`; native `xbps-*` tools must be in PATH,
or selected with `--xbps-bin /path/to/bin`. Builds target AArch64/glibc.
For the complete FDS build workstation, [bootstrap](getting-started.md) already
keeps `vendor/void-packages` as a clean upstream reference and prepares the
writable `.host/void-packages` checkout plus `.host/xbps/usr/bin`. The native
builder defaults to `.host/void-packages`. Reuse its local tools:
```sh
./tools/prepare-image-tools
./tools/in-void xbps-install -y qemu-img
fds-cartridge --image-tool-runner tools/in-image-tools doctor
fds-emulator doctor --qemu-runner tools/in-void
```
An optional runner receives a utility name followed by its arguments. Image
runners select EROFS utilities; XBPS runners provide the package-installation
namespace. Without runners, the tools use native utilities and bubblewrap.
## Build the example cartridge
The repository includes hello and system-report Void source packages. Each
software recipe names a package, its source-template directory and its public
commands. A cartridge recipe groups those software recipes into payloads.
With native tools and a prepared checkout:
```sh
fds-cartridge --void-packages /path/to/void-packages \
create examples/software/cartridge.toml out/demo-tools.img
fds-cartridge inspect out/demo-tools.img
```
With the project-local Void and image tools, use this equivalent command:
```sh
fds-cartridge --image-tool-runner tools/in-image-tools \
--xbps-tool-runner tools/in-void --xbps-bin "$PWD/.host/xbps/usr/bin" \
create examples/software/cartridge.toml out/demo-tools.img
fds-cartridge --image-tool-runner tools/in-image-tools inspect out/demo-tools.img
```
Choose one route. Output paths must be new. The command builds each package
with `xbps-src`, installs the package and runtime dependencies into private
staging trees, verifies their architecture and integrity, and writes the complete
image. Source build logs go to standard error; the final result is JSON.
The example image contains:
| Partition | Contents |
| --- | --- |
| 1: `FDS_METADATA` | Cartridge identity and software catalogue |
| 2: `FDS_PAYLOAD02` | `programs/demo.hello/`, including `usr/bin/hello` and libraries |
| 3: `FDS_PAYLOAD03` | `programs/demo.report/`, including `usr/bin/report` and dependencies |
There are no new xz software bundles. Programs are ready to run directly from
EROFS. Inspection validates both GPT tables, filesystem contents and software
hashes, then prints partition and command details as JSON.
## Build a reusable installed-software directory
To separate package compilation from cartridge assembly:
```sh
fds-cartridge --xbps-tool-runner tools/in-void \
--xbps-bin "$PWD/.host/xbps/usr/bin" \
software build examples/software/hello/software.toml out/demo-hello
fds-cartridge software inspect out/demo-hello
```
The output contains `software.toml` and `root/`. A cartridge's `sources` list can
name this directory instead of a source recipe. For example:
```toml
format = 2
id = "my.tools"
name = "My tools"
version = "1.0"
[[payload]]
sources = ["../out/demo-hello"]
```
Paths are relative to the cartridge recipe. Put several sources in one list to
share a partition, or add `[[payload]]` sections for separate partitions. See
[the format reference](software-format.md) for complete source metadata and limits.
## Start FDS
Build matching boot inputs using [Build and start FDS](getting-started.md), or
copy a kernel, initramfs and SYSTEM image from an FDS build machine.
```sh
fds-emulator --session out/my-emulator start --qemu-runner tools/in-void
```
Omit `--qemu-runner` when QEMU is installed directly on the workstation. To use
copied boot images:
```sh
fds-emulator --session out/my-emulator start \
--kernel /path/to/kernel_2712.img \
--initramfs /path/to/fds-initramfs.img \
--system /path/to/fds-system-cli.img
```
The session directory is created private (0700). After stopping, reuse the same
directory with the same `start` command; an existing directory must contain a
valid stopped FDS session owned by you. Keep its absolute path short
(under 90 bytes) for Unix sockets. Start waits for the FDS prompt and uses two
emulated CPUs and 1024 MiB RAM; `--memory-mib 2048` increases memory. SYSTEM and
PROGRAM images are read-only. The session holds logs and any DATA overlays.
## Insert and run software
From the workstation:
```sh
fds-emulator --session out/my-emulator insert 01 out/demo-tools.img
fds-emulator --session out/my-emulator guest -- fds bay 01
fds-emulator --session out/my-emulator guest -- hello 'Hello from the emulator'
fds-emulator --session out/my-emulator guest -- fds run 01 -- demo.report:report
```
Discovery is asynchronous. Wait until the bay reports `MOUNTED READ ONLY` before
running commands. Direct commands return their output and exit status to the
caller. `fds run` starts a background job; read its output with:
```sh
fds-emulator --session out/my-emulator guest -- tail -20 /run/log/cartridged/current
```
Open an interactive shell with `fds-emulator --session out/my-emulator console`.
At `FDS>`, use `hello`, `report`, `fds bays` or other guest commands. **Ctrl-]**
detaches without stopping the VM. Detach before using `guest`, `eject` or ordinary
`stop`, since they also need the serial console.
All twelve virtual bays accept images. Simultaneously inserted PROGRAM cartridges
must have distinct cartridge IDs. Duplicate identities produce a guest error.
Command-name collisions are resolved as described in [Using cartridges](cartridges.md).
## Eject and stop
```sh
fds-emulator --session out/my-emulator eject 01
fds-emulator --session out/my-emulator status
fds-emulator --session out/my-emulator stop
```
Eject asks FDS to stop programs and release storage before removing the virtual
USB device. If FDS refuses, the cartridge stays attached. `stop` performs native
FDS shutdown and waits for QEMU to exit. Start the same session again:
```sh
fds-emulator --session out/my-emulator start --qemu-runner tools/in-void
```
Pass any custom boot-image or memory options again. This is a fresh boot with
empty cartridge bays. Console and QEMU logs are appended, DATA overlay files
remain in place, and `previous-*.json` preserves the previous session's image
and overlay mapping. Insert cartridges again as needed. A running session,
unknown directory, insecure permissions, or an outstanding control operation
prevents restart; the tool never clears a live session to make room.
`unplug BAY` simulates a physical pull, and `stop --force` cuts virtual power.
Use these only for deliberate failure simulation; DATA writes can be lost.
## Save emulator DATA
Inserting a DATA image creates a writable `data-<bay>-<id>.qcow2` overlay in the
session directory. Eject retains it. Reinserting the original image creates a
fresh overlay. To export the changed contents after eject or shutdown:
```sh
qemu-img convert -f qcow2 -O raw /path/to/session/data-02-ID.qcow2 out/saved-data.img
```
Keep its backing image at the recorded path until export completes. Never convert
an overlay while a running VM is using it. Personal session directories are
preserved by `make clean`; keep them under your own names, such as `my-emulator`.
## Write a cartridge to USB
Identify the intended whole USB drive, unmount its partitions, then preview:
```sh
fds-cartridge preview out/demo-tools.img /dev/sdX out/usb-preview.json
```
Replace `/dev/sdX` with the actual destination. The JSON identifies the drive,
capacity, image hash and exact confirmation phrase. Check those values before
writing: this replaces the selected drive's contents.
```sh
sudo /absolute/path/to/fds-cartridge write out/usb-preview.json \
--confirm 'COPY THE EXACT confirmation VALUE FROM THE PREVIEW'
```
The writer rechecks identity, writes the entire disk image, flushes it and verifies
readback. On a larger drive it moves the backup GPT to the end. A changed image
or reinserted drive requires a fresh preview. Use the image-tool runner for
preview and inspection if your EROFS utilities are in the local container.
To rehearse with a disposable regular file:
```sh
truncate -s "$(stat -c %s out/demo-tools.img)" out/disposable-usb.img
fds-cartridge preview out/demo-tools.img out/disposable-usb.img out/file-preview.json --file-target
fds-cartridge write out/file-preview.json --confirm 'COPY THE EXACT confirmation VALUE'
```
See [troubleshooting](troubleshooting.md) for failed builds, namespace errors,
busy consoles, invalid media and blocked ejects.