Userspace Overview
Relevant source files
- userspace/README.md
- userspace/core/file_structure/src/lib.rs
- build.rs
- Cargo.toml
- ci/manifests/components.toml
- docs/reference/generated/abi.md
Userspace in axiomOS is a set of small no_std, no_main Rust programs that run in ring 3 (x86_64) or EL0 (AArch64), talk to the kernel through a custom syscall ABI, and are packed into an ext2 root filesystem at build time. There is no libc; every program links the tiny runtime minilib.
Layout
The userspace/ directory is grouped by role. Package names stay stable.
| Directory | Role |
|---|---|
core/ | init, minilib, and file_structure (the rootfs model) |
tools/ | BPF loaders, the rk host CLI, the rk_bridge ROS2 bridge, and the UART forwarder |
demos/ | Executable examples and integration probes. Not production tools |
benchmarks/ | Measurement workloads and the verifier calibration driver |
ci/manifests/components.toml declares the exact workspace and rootfs disposition of every crate. Directory grouping alone does not promote a demo or benchmark into a shipped product surface.
How binaries become a rootfs
- Declare.
userspace/core/file_structureexports a constSTRUCTUREtree ofDirandFileentries. Files areKind::ExecutableorKind::Resource.validate()rejects empty,.,.., slash-containing and duplicate names. - Depend. The root
Cargo.tomllists every binary as an artifact dependency (artifact = "bin") for bothx86_64-unknown-noneandaarch64-unknown-none, with an_x86or_aarch64suffix on the dependency name, alloptionaland enabled by thex86_64_depsoraarch64_depsfeatures. - Copy.
build.rswalksSTRUCTURE. For each executable it reads the environment variableCARGO_BIN_FILE_<DEP>_<name>that Cargo sets, and copies the binary into a temporarydiskdirectory. A missing variable panics, so adding a file toSTRUCTUREwithout a matching dependency fails the build.Kind::Resourceis currently atodo!(). - Pack.
mke2fs -t ext2 -m 5 -d <dir> ... 20M. - Deliver. On x86_64 the image is attached as a VirtIO block device. On the Pi,
scripts/build/rpi5.shembeds it in the kernel.
The kernel mounts the image read-only (README: VFS + ext2, read-only) and loads /bin/init as the first process. Boot panics if /bin/init is missing.
Rootfs contents
From STRUCTURE:
| Path | Contents |
|---|---|
/bin | init, gpio_demo, pwm_demo, timeseries_demo, iio_demo, syscall_demo, sys_exit_demo, sched_switch_demo, sched_switch_export_demo, sched_switch_bridge_demo, rk_uart_forwarder, file_io_demo, fork_test, bpf_loader, signed_bpf_loader, benchmark, verifier_bench |
/dev/fd | Empty directory |
/var/tmp | Empty directory |
/var/lib/rkbpf/programs | Empty unless AXIOM_SIGNED_BPF_STARTUP_PATH is set at build time, which places startup.rbpf here. build.rs also writes /var/hello.txt |
safety_demo and the host tools rk and rk_bridge are not in the rootfs. safety_demo has artifact disposition none in the component manifest; rk_bridge and rk are host binaries.
The kernel exposes pinned BPF objects under /sys/fs/bpf/maps/... (for example /sys/fs/bpf/maps/sched_switch_events). Those paths are created at runtime by BPF_OBJ_PIN, not stored in the image.
Boot-time behavior of init
init first runs a series of diagnostic probes that print markers (for example USERCOPY_EFAULT_OK, NANOSLEEP_WAITQ_OK, PIPE_WAITQ_OK, LIFECYCLE_EXEC_WAIT_OK, BPF_FOREIGN_OWNER_DENY_OK), then spawns the BPF workload:
| Build | What init spawns |
|---|---|
| Default (signed) | /bin/signed_bpf_loader with BPF_CAP_PROGRAM_LOAD and BPF_CAP_MAP_READ |
bpf-unsigned-development | /bin/sched_switch_export_demo, then after 100 ms /bin/sched_switch_bridge_demo, each with a restricted capability set |
It then parks in pause(). See Core libraries for details.
Where things run
| Page | Covers |
|---|---|
| Core libraries | init, minilib, file_structure |
| Demos | Every demo program |
| Tools | Loaders, rk, rk_bridge, forwarder, wire protocol |
| Benchmarks | benchmark and verifier_bench |