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

Protocols & ABI

Relevant source files

axiomos exposes three wire-level contracts: the userspace syscall ABI, a newline-delimited JSON stream for bridging BPF events to a host (rk-bridge), and a binary UART control link between the Pi 5 and the Shrike RP2040 board. The feat/v0.5-runtime branch extends the syscall ABI with versioned managed-runtime commands, extends the control link with session/handoff messages, and adds a fourth contract: the framed debug-UART installer protocol.

Syscall ABI v1

The generated ABI catalog  contains only interfaces dispatched by shipped kernels; reserved numbers, verifier-known but undispatched helpers, and verifier-cost instrumentation are not supported ABI. Numbers are defined in kernel_abi and dispatched by dispatch_syscall in kernel/src/syscall/mod.rs . On x86_64 the entry is interrupt vector 0x80; on AArch64 it is the svc synchronous exception.

IDNameAvailability
1SYS_EXITall
3SYS_OPENall
5SYS_FSTATall
27SYS_MALLOCall
28SYS_FREEall
32SYS_ABORTall
35SYS_GETCWDall
36SYS_READall
37SYS_WRITEall
38SYS_WRITEVall
39SYS_LSEEKall
40SYS_CLOSEall
41SYS_MMAPall
42SYS_DUPall
43SYS_DUP2all
44SYS_PIPEall
50SYS_BPFall
51SYS_PWM_CONFIGRaspberry Pi 5
52SYS_PWM_WRITERaspberry Pi 5
53SYS_PWM_ENABLERaspberry Pi 5
54SYS_CLOCK_GETTIMEall
55SYS_NANOSLEEPall
56SYS_SPAWNall
57SYS_FORKall
58SYS_EXECVEall
59SYS_WAITPIDall
60SYS_DEBUGall
61SYS_ESTOPall
62SYS_SPAWN_RESTRICTEDall
63SYS_RESTRICT_BPF_CAPABILITIESall
64SYS_INTERRUPT_SLEEPall

Errors follow ADR-0002: subsystem errors stay typed and are mapped to Errno once, in the syscall adapter. Unknown syscalls return ENOSYS (checked by the UNKNOWN_SYSCALL_ENOSYS_OK gate marker) and bad user pointers return EFAULT.

sys_bpf surface

SYS_BPF multiplexes BPF commands: map create/lookup/update/delete (0 to 3), BPF_PROG_LOAD (5), pin/get (6, 7), attach/detach (8, 9), BPF_OBJ_GET_INFO_BY_FD (15), BPF_PROG_LOAD_ELF (36), BPF_RINGBUF_POLL (37), and axiomos extensions BPF_PROG_UNLOAD (101), BPF_MAP_DESTROY (102), BPF_OBJ_UNPIN (103). Map types, helpers and attach types are documented in the eBPF Subsystem section. Managed-runtime commands are listed below. Attach type numbers from kernel_abi:

IDAttach typeAvailability
1TIMERcloud and embedded
2GPIORaspberry Pi 5
3PWMdefined in kernel_abi; not listed as supported in the generated catalog
4IIORaspberry Pi 5
5SYS_ENTER (alias SYSCALL)cloud and embedded
6SYS_EXITcloud and embedded
7SCHED_SWITCHcloud and embedded

Managed-runtime commands (managed-runtime feature)

The branch adds independently versioned SYS_BPF commands (kernel_abi/src/managed.rs , recorder.rs ). They are dispatched by syscall/managed.rs  before the legacy full-BpfAttr size check, require BPF_CAP_BEHAVIOR_ADMIN, copy bounded caller buffers, and reject unknown versions, wrong sizes and nonzero reserved fields. MANAGED_ADMIN_VERSION is 1.

IDCommandPurpose
256BPF_MANAGED_UPLOAD_BEGINStart one process-owned upload (at most 256 KiB)
257BPF_MANAGED_UPLOAD_CHUNKCopy one chunk (MANAGED_UPLOAD_CHUNK_BYTES = 256) at an exact offset; exact retries are idempotent
258BPF_MANAGED_UPLOAD_FINALIZEHand the upload to the preparation worker; returns an operation ID
259BPF_MANAGED_OPERATION_QUERYQuery an operation or one of four terminal receipts
260BPF_MANAGED_CANCELCancel an upload, preparation or rearm by ID
261BPF_MANAGED_ACTIVATEActivate an exact staged artifact at an expected generation
262BPF_MANAGED_ROLLBACKActivate the exact retained previous artifact with fresh state
263BPF_MANAGED_SLOT_QUERYSlot status (version 1) or one retained artifact’s full identity (version 2)
264BPF_MANAGED_INSTALLATION_CANCELCancel an exact lifecycle request
265BPF_MANAGED_DEACTIVATEDeactivate the active artifact through the safe boundary
266BPF_MANAGED_RETIRERetire an exact inactive artifact
267BPF_MANAGED_RECORDER_STATUSRecorder window, clock and loss counters
268BPF_MANAGED_RECORDER_READRead at most two 96-byte records from a cursor
269BPF_MANAGED_REARMExplicit link requalification after a stop

Operation states are IDLE, UPLOADING, QUEUED, PREPARING, RESIDENT, FAILED, CANCELLED, STAGED, HANDOFF, COMMITTED and CLEANUP. Lifecycle targets are CANDIDATE (1), PREVIOUS (2), DEACTIVATE (3), RETIRE (4) and REARM (5). The generated ABI catalog also lists helper 1009 (BPF_HELPER_MANAGED_MOTOR_PAIR_V1, managed interpreter only), which captures one wheel-pair request and never actuates directly. Managed programs receive the 56-byte ManagedControlContextV1 (version, size, cycle_id, scheduled/actual/sensor timestamps in ns, raw sensor_value, sensor_valid). Details are in Lifecycle and Executor.

BPF_CAP_BEHAVIOR_ADMIN (1 << 11) is a new capability bit. It grants no legacy load, attach, map mutation, privileged verification or actuation authority, and legacy init does not receive it.

rk-bridge NDJSON stream

rk_bridge resolves pinned BPF objects through sys_bpf, which only exists on a running axiomos kernel. To get the same events into a ROS 2 graph on a developer host, rk_uart_forwarder (running on axiomos, launched from init) writes newline-delimited JSON to stdout, which is the UART, and the host runs rk-to-ros --input stdin.

socat /dev/ttyUSB0,raw,echo=0,b1500000 STDOUT \ | rk-to-ros --input stdin --topic /rk/sched_switch
RecordKindFields
metacontrol, first recordprotocol (currently 1), source (attach point name)
readycontrol, optionalmap_fd returned by BPF_OBJ_GET
sched_switcheventseq, cpu, prev_pid, prev_tid, next_pid, next_tid (all u64)
{"type":"meta","protocol":1,"source":"sched_switch"} {"type":"ready","map_fd":3} {"type":"sched_switch","seq":1,"cpu":0,"prev_pid":1,"prev_tid":1,"next_pid":2,"next_tid":2}

Consumer rules: skip lines not starting with { (tolerates kernel log noise), reject an unknown protocol version, ignore unknown record types and fields, treat a malformed line as recoverable, and stop cleanly at end of stream. There is no reliability layer: at 1.5 Mbit/s and about 120 bytes per event the ceiling is roughly 1500 events/s, and seq only detects gaps. Records for sys_enter, sys_exit, gpio, pwm and iio are anticipated but must be added to WireRecord before a host can decode them. The protocol is explicitly a stop-gap; the long-term target is Ethernet (#67) with a DDS-shaped transport.

shrike_link is a no_std, zero-dependency, host-tested codec shared by the kernel (Pi 5 side) and the RP2040 firmware.

SYNC(0x7E) VER(0x01) TYPE LEN PAYLOAD[LEN] CRC_LO CRC_HI CRC16-CCITT/FALSE over VER, TYPE, LEN, PAYLOAD; MAX_PAYLOAD = 16

The decoder is a byte-fed state machine: only the Idle state treats 0x7E as SYNC; once LEN is accepted, payload and CRC are consumed by count, so no byte stuffing is needed. LEN must match each type’s fixed payload length.

TYPEDirectionMessage
0x01Pi 5 to ShrikeMotorSetpoint { seq, left, right }, signed per-mille duty (-1000 to 1000), not clamped by the codec
0x02Pi 5 to ShrikeEstop { assert }, advisory soft e-stop
0x03Pi 5 to ShrikeHeartbeatToShrike { seq }
0x81Shrike to Pi 5Sensor { ultrasonic_echo_us, estop_line, flags }
0x82Shrike to Pi 5HeartbeatToPi { seq }
0x04Pi 5 to ShrikeSessionOffer { session } (branch)
0x05Pi 5 to ShrikeSafeBarrier { session, correlation, sequence } (branch)
0x06Pi 5 to ShrikeRequalify { session } (branch)
0x83Shrike to Pi 5SessionReady { session } (branch)
0x84Shrike to Pi 5SafeAck { session, correlation, sequence } (branch)
0x85Shrike to Pi 5Prepared { session } (branch)

The branch messages carry a little-endian u32 session (4-byte payload) or session, u64 correlation and u8 sequence (13-byte payload). A zero session or correlation is rejected as BadIdentity on both encode and decode. They implement the acknowledged safe handoff (barrier/ack) and explicit rearm (Requalify, Prepared, Offer, Ready); see Handoff & Rearm.

Safety behavior lives in pure, host-tested state machines rather than in drivers:

  • Shrike-side watchdog (watchdog.rs): output is SafeStop on cold start, after command silence beyond a timeout (heartbeats cannot extend motor authority), and while a soft e-stop is latched. A MotorSetpoint whose seq is not newer than the last accepted one is rejected (anti-replay).

  • Pi-side session (session.rs): heartbeats the RP2040 while inbound traffic is alive; when inbound goes silent it stops heartbeating (so the RP2040 watchdog trips) and retries a one-shot e-stop every tick until the transport confirms it was sent.

  • Handoff state (handoff.rs, branch): shared by the Pi poller and the managed timer. At a release boundary it inhibits the old controller, discards unsent motion, sends a prioritized SafeBarrier after any started frame, and only a matching SafeAck received after whole-frame transmission produces a publication receipt. The 80 ms handoff timeout includes the acknowledgement wait.

Installer frames (debug UART, branch)

installer.rs is a separate fixed-size transport, outside the actuator message namespace, used between host rk runtime and the in-guest installer over the Pi debug UART.

MAGIC(0xA5) KIND<<4|LEN SEQ[4, LE] DATA[8] CRC16[2, LE] = 16 bytes KIND: Request(0) RequestLast(1) Poll(2) Reset(3) Ack(8) Response(9) ResponseLast(10) Error(11) MAX_MESSAGE_BYTES = 320

One frame is sent per credit; the installer replays exact duplicate requests without redispatch, rejects stale or altered identities, and requires Reset after receive faults. See Installer.

The codec deliberately does not clamp duty. The kernel actuation monitor and the FPGA safety gate are the single clamp source of truth; the hard e-stop is an independent FPGA line.

Last updated on