Skip to Content
Viewing feat/v0.5-runtime docs — not the stable branch. Changes vs main · Go to main

Tools

Relevant source files

The tools split into two groups. bpf_loader, signed_bpf_loader and rk_uart_forwarder are guest binaries shipped in the rootfs. rk (the rk_cli package) and rk_bridge (binary rk-to-ros) are standalone host packages excluded from the kernel workspace.

ToolWhere it runsArtifact (components.toml)
bpf_loaderGuest, /binrootfs:bpf_loader
signed_bpf_loaderGuest, /binrootfs:signed_bpf_loader
rk_uart_forwarderGuest, /binrootfs:rk_uart_forwarder
rk_cliHosthost:rk
rk_bridgeHosthost:rk-to-ros

bpf_loader

An end-to-end BPF demo using hand-assembled instructions, with a banner “axiomos BPF end-to-end demo: Maps + Ringbuf + Timer Attach”. It runs five steps: create an array map for a tick counter (key 4 bytes, value 8, one entry), create a ringbuf, load a program that increments the counter and writes to the ringbuf, attach it to the timer, then poll until it has seen 10 events and read the counter back through MAP_LOOKUP_ELEM. It also exercises the privileged load and unload paths. It uses helpers such as map lookup, ringbuf_output, trace_printk and kernel heap statistics. It submits raw instructions through BPF_PROG_LOAD; whether the kernel accepts them depends on the signing policy of the build (see signed_bpf_loader for the signed-container path).

signed_bpf_loader

The loader that init spawns in default (signed) builds. With the managed-runtime feature it becomes the managed-runtime installer instead (see Managed-runtime installer mode). The description below is the default, legacy mode.

  1. Opens /var/lib/rkbpf/programs/startup.rbpf. On failure prints SIGNED_BPF_INPUT_MISSING and exits 2.
  2. Reads up to 256 KiB. An empty or full buffer prints SIGNED_BPF_INPUT_INVALID (exit 4).
  3. Calls BPF_PROG_LOAD_ELF with the container bytes. A rejection prints SIGNED_BPF_LOAD_REJECTED (exit 5).
  4. On success prints SIGNED_BPF_LOAD_OK and parks in pause().

The container must be signed (Ed25519) and verified by the kernel against the embedded trust key. Bake one into the image with AXIOM_SIGNED_BPF_STARTUP_PATH at build time, as described in Building. The input path comment notes that rk deploy --program startup.rbpf places the signed container here.

Managed-runtime installer mode

The package has a lib.rs plus two features: managed-runtime (pulls in shrike_link and zerocopy) and managed-syscall-probe (implies managed-runtime). Without managed-runtime, the binary is the legacy loader above. With it, the binary does not read startup.rbpf. Instead it runs a UART service loop:

  1. Transport (from lib.rs) reads bytes from fd 0, feeds them to the shrike_link installer frame Decoder, and keeps the encoded reply in a fixed buffer that is written to fd 1 until fully sent.
  2. For each decoded request, dispatch forwards the message to the kernel through minilib::managed_bpf_bytes (the managed SYS_BPF commands). A stop command (0xffff) calls estop_trigger.
  3. Read errors other than EAGAIN and EINTR reset the endpoint and decoder, and an idle loop sleeps 1 ms.

managed-syscall-probe replaces the service loop with an opt-in QEMU probe (syscall_probe.rs) that runs the real installer binary with real syscalls, credentials, process memory and the preparation worker before any UART service. It prints MANAGED_SYSCALL_PROBE_FAIL stage=... observed=... expected=... and exits 1 on a mismatch. The protocol, framing and kernel side are described in Installer.

rk (rk_cli)

A host CLI for signing and managing BPF programs. The package is rk-cli, the binary rk. Global flags are -v/--verbose and -c/--config. Configuration lives in ~/.config/rkbpf (trusted keys in trusted_keys).

CommandPurpose
rk key generate -o PREFIXNew Ed25519 keypair, writes PREFIX.pub and PREFIX.key
rk key export -k FILE [-f pem or der or raw]Export a public key
rk key import -k FILE -a ALIASImport a trusted public key
rk key listList trusted keys
rk sign -i OBJ.o -k KEY [-o OUT.rbpf]Sign a BPF object into a container
rk verify -i FILE [-k PUB or -t TRUSTED_DIR]Verify a signed program, or an AXMB managed bundle (see below)
rk build -s SRC [-o DIR] [-p cloud or embedded]Build a BPF program (default profile embedded)
rk deploy -p FILE... [-t TARGET] [-a ATTACH]Deploy signed programs; target localhost (default), ssh://user@host or user@host
rk list, rk unload -p ID, rk info -i FILEInspect and manage
rk init NAME [-p PROFILE]Scaffold a project
rk bundleSign normalized raw BPF instructions as a managed AXMB bundle
rk runtime --port DEV COMMANDUpload and administer managed controllers over the debug UART
rk audit-decode INPUT --output FILEDecode a saved bounded audit export offline

Deploy refuses files that do not start with the RBPF magic. Local deploy copies into /var/lib/rkbpf/programs/ as NAME.rbpf. Signing uses ring.

rk bundle

Signs a managed controller bundle. The input is normalized raw little-endian BPF instructions, not an ELF object (the managed examples build these with llvm-objcopy, see Demos). The key is an Ed25519 PKCS#8 private key. The output path must be new.

FlagMeaning
--input, --output, --keyRaw instruction file, new AXMB path, PKCS#8 private key
--behavior-idLogical behavior ID, exactly 32 hexadecimal characters
--revisionSigner-provided behavior revision
--motor-pairRequest permission to emit one motor pair per invocation
--envelopeDeclare the read-only envelope at local handle 0
--array-value-size, --array-entriesDeclare one private ARRAY (each requires the other)

It prints the behavior ID, revision, signer public key and fingerprint, payload and bundle digests and size, then states that it only signed: kernel authentication, verification, admission and activation have not run. Format and trust rules are in Managed bundles.

rk runtime

rk runtime --port /dev/ttyACM0 COMMAND talks to the installer over a Linux serial character device. It configures the port with stty at 115200 baud, raw mode and no flow control, and sends one bounded request at a time. The first exchange is a reset frame whose challenge must be echoed back. If a response times out, the tool warns that the outcome may be unknown and tells you to query the slot or operation before retrying.

CommandPurpose
query [--operation ID or --artifact N]Slot status, a retained operation (0 selects the latest), or an artifact
upload BUNDLEUpload a canonical signed bundle in chunks, then enqueue preparation
activate, rollback, deactivate, retireLifecycle requests; each needs --expected-generation and --artifact
cancel ID [...]Cancel an upload, preparation or rearm by ID, or an exact lifecycle request (with --expected-generation, --artifact, --target-kind 1 to 4)
rearm --expected-generation NRequalify the stopped link; leaves the controller inhibited until activation
stopTrigger trusted stop; does not unload code or release e-stop
audit-statusRetained audit interval, clock and explicit loss counters
audit-export --output FILEExport one bounded audit interval as JSON lines, including gaps

Lifecycle commands carry the generation you observed, so a stale view is rejected rather than applied. See Lifecycle, Handoff and rearm and Recorder.

rk audit-decode and rk verify

rk audit-decode INPUT --output FILE interprets a saved audit export without opening a serial device. The input is capped at 8 MiB and must end with a newline; the output file must be new. The source calls it offline interpretation of one retained window, never a qualification verdict.

rk verify reads the first magic bytes. If they are the managed magic, it authenticates the AXMB bundle against the full trusted signer key (from -k or the trusted-keys directory), enforces the 256 KiB bundle limit, and prints the identity as JSON. It then states that bytecode verification and live admission are still required, so a pass here is authentication only.

CI lints and tests rk_cli and rk_bridge as standalone workspaces. Whether the end-to-end sign, bake, boot loop is exercised on hardware is manual.

rk_uart_forwarder

A guest binary that opens the pinned /sys/fs/bpf/maps/sched_switch_events ringbuf with BPF_OBJ_GET, polls it with BPF_RINGBUF_POLL (backing off 10 ms when empty), and prints newline-delimited JSON to stdout. On the Pi, stdout is the UART when launched from init. If BPF_OBJ_GET fails it prints “run sched_switch_export_demo first” to fd 2 and exits 1. It emits a meta banner, a ready record, then one sched_switch record per event with a monotonically increasing seq. Protocol version is 1.

rk_bridge (rk-to-ros)

A host program that bridges events to stdout or ROS2. It is its own Cargo package (with [workspace]) and has a fuzz target (rk_bridge/fuzz, event_stream).

OptionDefaultMeaning
-m, --map PATH/sys/fs/bpf/maps/sched_switch_eventsPinned ringbuf (needs axiomOS sys_bpf)
-t, --topic/rk/eventsROS2 topic; with the default, events route to /rk/imu, /rk/motor, /rk/safety, /rk/gpio, /rk/timeseries, /rk/sched_switch, /rk/trace
--stdoutoffPrint instead of publishing
-f, --formatjson-linesjson, json-lines or text
-r, --rate-limit0Events per second, 0 is unlimited
--poll-interval10Milliseconds
--event-kindsched-switchlegacy or sched-switch
--input stdinnoneRead JSON lines from stdin instead of a pinned object
--demooffSynthetic events

Real ROS2 publishing requires the Cargo feature ros2, which uses the host ros2 CLI and publishes std_msgs/msg/String. Without it, construction of the ROS publisher does not publish to a real graph.

Wire protocol

The forwarder and rk-to-ros --input stdin talk over a line-oriented JSON protocol (docs/architecture/protocols/rk-bridge.md). It is deliberately minimal, a stopgap until a faster transport such as Ethernet exists.

# Host side socat /dev/ttyUSB0,raw,echo=0,b1500000 STDOUT \ | rk-to-ros --input stdin --topic /rk/sched_switch
RecordFieldsNotes
metatype, protocol (u32), sourceFirst record each session. Consumer aborts if protocol is not 1
readytype, map_fdOptional, after the pinned object is opened
sched_switchtype, seq, cpu, prev_pid, prev_tid, next_pid, next_tidseq is forwarder-assigned; 0 if absent

Consumer rules: skip lines not starting with {; tolerate unknown records and fields; treat a malformed line as recoverable; treat end of stream as clean. There is no reliability layer: the doc estimates a ceiling near 1500 events per second at 1.5 Mbit/s and notes that seq lets you detect, not recover, gaps. Serial-port input is a stated follow-up; only stdin is supported today.

Last updated on