Skip to Content
axiomOSmainArchitectureProtocols & ABI

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.

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. 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

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 }

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.

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