233 lines
9.2 KiB
Markdown
233 lines
9.2 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`. Build them with Rust,
|
|
a C linker and Make; the repository selects its pinned Rust toolchain:
|
|
|
|
```sh
|
|
make workstation
|
|
mkdir -p out
|
|
export PATH="$PWD/out/workstation:$PATH"
|
|
```
|
|
|
|
| Prerequisite | Used for |
|
|
| --- | --- |
|
|
| Prepared 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. 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
|
|
prepares `vendor/void-packages` and `.host/xbps/usr/bin`. 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 must be new and private. 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. Session logs remain available; create a new session to boot again.
|
|
|
|
`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.
|