Strat9 OS
An experimental operating system kernel written in Rust, targeting x86_64 (primary) and aarch64 (secondary).
Quick start
Building from source
# Build the full OS image (bootloader + kernel)
cargo make build-all
# Run in QEMU
cargo make run-gui
# Run with SMP (multi-core)
cargo make run-gui-smp
Architecture guides
| Guide | Description |
|---|---|
| Architecture Overview | Kernel subsystems, design principles, and data flow diagrams |
| Silo System | Process isolation, resource limits, pledge/unveil, module loading |
| Memory Management | Buddy allocator, slab heap, COW, page tables, vmalloc |
| Boot Sequence | BIOS → bootloader → UEFI bootloader → kernel init flow |
| IPC Mechanisms | Channels, shared rings, semaphores, futexes |
| IPC Transport Architecture | 3-level hybrid IPC model (TypeSafe / LockFree / MMU) |
| Driver Model | Component trait, PCI, NIC, storage, USB drivers |
| Syscall Reference | Complete syscall table with parameters and errors |
| ABI Overview | Kernel/userspace ABI definitions and versioning |
| ABI Changelog | Recent ABI changes (auto-generated) |
| ABI Support Matrix | Syscall and struct compatibility matrix |
| Syscall Layer | Userspace syscall wrappers and error handling |
| Changelog | Project changelog (auto-generated from git) |
| Publishing | Build, release, and deployment instructions |
API reference by category
Core
The kernel, ABI definitions, and bootloader : the foundation of the OS.
| Crate | Description | API |
|---|---|---|
| strat9-kernel | OS kernel: scheduler, memory management, drivers, IPC | docs · source |
| strat9-abi | ABI definitions shared between kernel and userspace (syscalls, data structs, flags, errno) | docs · source |
| strat9-bootloader | BIOS/UEFI bootloader: stage1 MBR, stage2 protected/long mode switch | docs · source |
Syscall & Userspace
Userspace libraries for interacting with the kernel.
| Crate | Description | API |
|---|---|---|
| strat9-syscall | High-level syscall wrappers, error mapping, and constants | docs · source |
| strate-init | Init process: system bootstrap and service management | docs · source |
Component Framework
Trait-based component model for drivers and services.
| Crate | Description | API |
|---|---|---|
| component | Component trait and registration framework | docs · source |
| component-macro | Derive macros for component registration | docs · source |
| strat9-bus-drivers | Bus driver infrastructure (PCI, VirtIO) | docs · source |
| strate-bus | Bus abstraction layer | docs · source |
Network Drivers
Intel Ethernet and NIC queue management.
| Crate | Description | API |
|---|---|---|
| e1000 | Intel E1000/E1000e network driver | docs · source |
| intel-ethernet | Intel Ethernet common register definitions | docs · source |
| driver-net-proto | Network protocol driver abstractions | docs · source |
| nic-queues | NIC TX/RX queue management | docs · source |
| nic-buffers | NIC buffer allocation and management | docs · source |
| net-core | Network core utilities | docs · source |
Filesystem
Filesystem abstraction and implementations.
| Crate | Description | API |
|---|---|---|
| strate-fs-abstraction | Filesystem abstraction layer with safe math and Unicode | docs · source |
| strate-fs-ext4 | ext4 filesystem implementation | docs · source |
| strate-fs-ramfs | In-memory RAM filesystem | docs · source |
Networking
Network stack, silo network service, and tools.
| Crate | Description | API |
|---|---|---|
| strate-net | Network stack (TCP/UDP/ICMP) | docs · source |
| strate-net-silo | Network silo service (TCP/UDP listener) | docs · source |
| dhcp-client | DHCP client status monitor | docs · source |
| ping | ICMP ping utility | docs · source |
| udp-tool | UDP scheme test utility | docs · source |
| telnetd | Telnet server | docs · source |
| ice-candidate | ICE candidate discovery over scheme UDP | docs · source |
System Services
Admin interfaces, compatibility layers, and experimental features.
| Crate | Description | API |
|---|---|---|
| strat9-components-api | Shared component API types and traits | docs · source |
| strate-console-admin | Interactive console shell with silo management | docs · source |
| strate-web-admin | Web-based admin interface | docs · source |
| strate-wasm | WebAssembly runtime support | docs · source |
| strate-webrtc | WebRTC support | docs · source |
| musl-compat | musl libc compatibility layer | docs · source |
| alloc-freelist | Free-list allocator | docs · source |
Testing
| Crate | Description | API |
|---|---|---|
| silo-test | Silo integration tests | source |
| mem-test | Memory subsystem tests | docs · source |
| test-syscalls | Syscall integration tests | docs · source |
| test-exec | Exec syscall tests | docs · source |
Building docs locally
# Build the full docs site (mdBook + rustdoc)
bash tools/scripts/build-docs-site.sh
# Serve locally
python3 -m http.server --directory build/docs-site 8000
# Check for broken links
python3 tools/scripts/check-links.py --site-dir build/docs-site
Architecture Overview
Strat9 OS is a microkernel-inspired operating system written in Rust. The kernel runs in Ring 0 on x86_64, with userspace processes isolated through capability-based security and silo boundaries.
Kernel subsystems
graph TB
subgraph "Ring 0 : Kernel"
BOOT[Boot / Init]
SCHED[Scheduler]
MEM[Memory Manager]
IPC[IPC]
CAP[Capability System]
SILO[Silo Manager]
VFS[VFS]
SYSCALL[Syscall Entry]
HW[Hardware Drivers]
end
subgraph "Ring 3 : Userspace"
INIT[Init Process]
APP[Applications]
SVC[Services]
end
BOOT --> SCHED
BOOT --> MEM
SYSCALL --> SCHED
SYSCALL --> MEM
SYSCALL --> IPC
SYSCALL --> CAP
SYSCALL --> VFS
SCHED --> MEM
IPC --> MEM
IPC --> CAP
SILO --> CAP
SILO --> MEM
VFS --> MEM
HW --> SYSCALL
APP --> SYSCALL
SVC --> SYSCALL
INIT --> SYSCALL
SILO -.-> APP
SILO -.-> SVC
Subsystem summary
| Subsystem | Module | Purpose |
|---|---|---|
| Boot | boot/ | Assembly stubs (16→64 bit), UEFI bootloader handoff, early init |
| Scheduler | process/scheduler/ | Per-CPU run queues, multi-class scheduling (RT FIFO/RR, Normal, Idle) |
| Memory | memory/ | Buddy allocator, slab heap, COW, page tables, vmalloc |
| IPC | ipc/ | 3-level Transport Manager (TypeSafe/LockFree/MMU), typed channels, shared rings, semaphores |
| Capability | capability.rs | Unforgeable tokens, per-resource refcounting |
| Silo | silo/ | Process isolation containers with resource quotas |
| VFS | vfs/ | Virtual filesystem with scheme-based I/O |
| Syscall | syscall/ | Syscall dispatch and validation |
| Drivers | hardware/ | NIC, storage, USB, GPU, VirtIO, PCI |
Design principles
-
Capability-based security : All kernel resources (memory, IPC ports, devices) are accessed through unforgeable
CapIdtokens. No raw pointers leak to userspace. -
Silo isolation : Processes run inside silos (analogous to containers). Each silo has memory quotas, capability boundaries, and IPC restrictions. Policy lives in userspace; the kernel enforces mechanisms.
-
Per-CPU scheduling : Each CPU core has its own run queue set. Tasks are pinned to cores via the scheduler, avoiding cross-core lock contention on the hot path.
-
COW-first memory :
fork()shares pages via copy-on-write. Physical frames are freed only when the last reference disappears. Buddy allocator provides O(1) order-0 allocations via per-CPU caches. -
Scheme-based I/O : Filesystem operations go through a VFS layer that routes to scheme handlers (like Plan 9). Userspace can implement custom schemes for devices, networks, and IPC.
Data flow: userspace syscall
sequenceDiagram
participant U as Userspace
participant K as Syscall Entry
participant C as Capability Check
participant S as Subsystem
U->>K: syscall(SYS_READ, fd, buf, len)
K->>K: Validate args, disable IRQs
K->>C: Resolve CapId from fd
C->>C: Check permissions (read)
C->>S: Dispatch to VFS read
S->>S: Page fault / buffer I/O
S-->>K: Return bytes read
K-->>U: Result in RAX
Silo System
Silos are Strat9 OS's primary isolation mechanism. Each silo is a container for processes with bounded resources, restricted capabilities, and filesystem access control. Policy lives in userspace; the kernel enforces mechanisms.
Overview
graph TB
subgraph "Admin (Silo Admin)"
ADMIN[Console Admin / Web Admin]
end
subgraph "Kernel"
SM[Silo Manager]
CAP[Capability System]
MEM[Memory Accounting]
EV[Event Queue]
end
subgraph "Silo 1 (System)"
T1[Task A]
T2[Task B]
end
subgraph "Silo 2 (User)"
T3[Task C]
end
ADMIN -->|create/config/start/stop| SM
SM --> CAP
SM --> MEM
SM --> EV
T1 --> CAP
T2 --> CAP
T3 --> CAP
T1 -.-> MEM
T3 -.-> MEM
Silo identity
Each silo has a numeric ID (SiloId) and a tier derived from the ID range:
| ID range | Tier | Purpose |
|---|---|---|
| 1–9 | Critical | Kernel system services (init, logger) |
| 10–999 | System | Drivers, filesystems, network |
| 1000+ | User | User applications |
User-tier silos cannot have hardware or control permissions (enforced at creation).
Silo lifecycle
stateDiagram-v2
[*] --> Created : SYS_SILO_CREATE
Created --> Ready : SYS_SILO_CONFIG + SYS_SILO_ATTACH_MODULE
Ready --> Running : SYS_SILO_START
Running --> Paused : SYS_SILO_SUSPEND
Paused --> Running : SYS_SILO_RESUME
Running --> Stopping : SYS_SILO_STOP (graceful)
Stopping --> Stopped : tasks exit
Running --> Stopped : SYS_SILO_KILL (force)
Stopped --> [*] : SYS_SILO_DESTROY
Running --> Crashed : fault / panic
Crashed --> Stopped : cleanup
States: Created → Loading → Ready → Running → Paused → Stopping → Stopped → Destroyed (also Crashed, Zombie)
Resource limits
The SiloConfig struct defines per-silo resource bounds:
SiloConfig {
mem_min: u64, // Minimum guaranteed memory (bytes)
mem_max: u64, // Maximum allowed memory (0 = unlimited)
cpu_shares: u32, // CPU share weight (for proportional scheduling)
cpu_quota_us: u64, // CPU time quota per period (microseconds)
cpu_period_us: u64, // Quota period (microseconds)
cpu_affinity_mask: u64, // CPU affinity bitmask
max_tasks: u32, // Maximum concurrent tasks
io_bw_read: u64, // Read bandwidth limit
io_bw_write: u64, // Write bandwidth limit
flags: u64, // Feature flags
family: u8, // Silo family (SYS/DRV/FS/NET/WASM/USR)
}
Memory accounting: Every allocation through the kernel heap charges against the silo's mem_usage_bytes. When mem_max is exceeded, charge_current_task_memory() returns OutOfMemory. This is enforced transparently in the buddy allocator and slab paths.
Octal mode (pledge/unveil)
Access control uses an OctalMode with three permission groups, encoded as a 12-bit octal value:
| Bits | Group | Permissions |
|---|---|---|
| 8–10 | Control | LIST (0b100), STOP (0b010), SPAWN (0b001) |
| 5–7 | Hardware | INTERRUPT (0b100), IO (0b010), DMA (0b001) |
| 2–4 | Registry | LOOKUP (0b100), BIND (0b010), PROXY (0b001) |
Example: 0o755 = LIST+STOP+SPAWN + INTERRUPT+IO + LOOKUP+BIND
Pledge
SYS_SILO_PLEDGE(mode) restricts the silo's permissions. The new mode must be a subset of the current mode : escalation is rejected with PermissionDenied.
silo.mode.pledge(new_mode):
if !new_mode.is_subset_of(self.mode):
return PermissionDenied // escalation attempt
self.mode = new_mode
Unveil
SYS_SILO_UNVEIL(path, rights) restricts filesystem access to specific paths with read/write/execute permissions. Multiple rules can be added; if a path matches no rule, access is denied.
Rights bits: read (0x1), write (0x2), execute (0x4)
Matching: A rule /srv/data matches /srv/data/file.txt but not /srv/other. Rule / matches everything.
Enter Sandbox
SYS_SILO_ENTER_SANDBOX() is irreversible : it clears all registry permissions and prevents further capability grants. Once sandboxed, the silo cannot escape.
Family types
| Family | Value | Purpose |
|---|---|---|
SYS | 0 | System services (init, admin) |
DRV | 1 | Hardware drivers |
FS | 2 | Filesystem handlers |
NET | 3 | Network stack |
WASM | 4 | WebAssembly runtime |
USR | 5 | User applications |
Feature flags
| Flag | Value | Description |
|---|---|---|
SILO_FLAG_ADMIN | 1 << 0 | Silo has admin capabilities |
SILO_FLAG_GRAPHICS | 1 << 1 | Graphics session support |
SILO_FLAG_WEBRTC_NATIVE | 1 << 2 | WebRTC native support (requires GRAPHICS) |
SILO_FLAG_GRAPHICS_READ_ONLY | 1 << 3 | Graphics read-only mode |
SILO_FLAG_WEBRTC_TURN_FORCE | 1 << 4 | Force TURN relay for WebRTC |
Module system (CMOD)
Silos can load code modules in the CMOD binary format:
Strat9ModuleHeader {
magic: "CMOD",
version: 1 or 2,
cpu_arch: 0 (x86_64),
flags: MODULE_FLAG_SIGNED | MODULE_FLAG_KERNEL,
code_offset/size, data_offset/size, bss_size,
entry_point,
export/import/relocation tables,
key_id, signature,
cpu_features_required (v2+),
}
Loading flow:
- Admin calls
SYS_MODULE_LOADwith a blob (from file, IPC stream, or initfs path) - Kernel validates the header (magic, version, alignment, signature)
- Module is registered in the global
ModuleRegistry - Admin calls
SYS_SILO_ATTACH_MODULEto bind the module to a silo - Admin calls
SYS_SILO_STARTto launch the silo's entry point
Events
The kernel pushes events to a fixed-capacity ring buffer (256 entries). Userspace polls via SYS_SILO_EVENT_NEXT.
| Event | Trigger |
|---|---|
Started | Silo started or config updated |
Stopped | Graceful stop |
Killed | Force kill |
Crashed | Fault/panic (data0 encodes fault reason + subcode) |
Paused | Suspended |
Resumed | Resumed from pause |
Crash encoding: data0 = fault_reason | (subcode << 16)
PageFault (1),GeneralProtection (2),InvalidOpcode (3)
Silo admin
Admin operations require a ResourceType::Silo capability with grant permission. The SILO_ADMIN_RESOURCE (resource 0) is the special admin handle.
Bootstrap: The init process receives a silo-admin capability at boot via create_silo_admin_capability().
Admin operations:
| Operation | Function | Description |
|---|---|---|
| Create | kernel_spawn_strate() | Register module, create silo, spawn task |
| Start | kernel_start_silo() | Transitions Ready → Running |
| Stop | kernel_stop_silo() | Graceful stop (tasks exit) |
| Kill | kernel_stop_silo(force=true) | Force kill all tasks |
| Destroy | kernel_destroy_silo() | Remove silo (must be stopped) |
| Rename | kernel_rename_silo_label() | Change silo label |
| Pledge | sys_silo_pledge() | Restrict permissions |
| Unveil | sys_silo_unveil() | Restrict filesystem access |
| Sandbox | sys_silo_enter_sandbox() | Lock down (irreversible) |
Path-based label assignment
When a filesystem path matches /srv/strate-fs-<type>/<label>/, the label is automatically assigned to the silo. This allows convention-based service discovery.
Silo vs. other isolation mechanisms
| Mechanism | Scope | Enforcement |
|---|---|---|
| Silo | Process group + resources + capabilities | Kernel (SiloManager) |
| Capability | Individual resource access | Kernel (CapabilityManager) |
| Pledge | Syscall permission subset | Kernel (OctalMode) |
| Unveil | Filesystem path access | Kernel (UnveilRules) |
| Sandbox | Full lockdown (irreversible) | Kernel (sandboxed flag) |
Memory Management
Strat9 OS uses a layered memory architecture: physical frame allocation (buddy), kernel heap (slab), virtual memory (page tables + COW), and large-object allocation (vmalloc).
Memory hierarchy
graph TB
subgraph "Physical"
BUDDY[Buddy Allocator]
ZONES[Zone DMA / Normal / HighMem]
FRAMES[Physical Frames 4KiB]
end
subgraph "Kernel Heap"
SLAB[Slab Sub-allocator]
HEAP[GlobalAlloc Heap]
end
subgraph "Virtual Memory"
AS[Address Space / PML4]
VMA[Virtual Memory Regions]
COW[COW Pages]
PAGING[Page Tables]
end
subgraph "Large Objects"
VMALLOC[Vmalloc]
end
BUDDY --> FRAMES
ZONES --> BUDDY
FRAMES --> SLAB
SLAB --> HEAP
FRAMES --> PAGING
AS --> VMA
VMA --> COW
COW --> PAGING
FRAMES --> VMALLOC
Buddy allocator
The buddy allocator manages physical frames. It divides memory into zones (DMA, Normal, HighMem) and uses a free-list per order (0–10) for power-of-two allocations.
Key properties:
- Order-0 allocations go through per-CPU caches (
LOCAL_FRAME_CACHES) for O(1) fast path - Cross-CPU stealing when local cache is empty
- Compaction assist when high-order allocations fail
- Refcount sentinel: free-list frames carry
REFCOUNT_UNUSED(u32::MAX)
alloc order-0:
1. Try local cache (per-CPU, PreemptDisabled lock)
2. Refill cache from buddy global
3. Steal from other CPU caches
4. Fallback to global buddy allocator
Slab heap
The kernel heap uses a slab sub-allocator for small objects (≤ 2048 bytes). Each size class has its own free list backed by whole pages from the buddy allocator.
| Class size | Typical use |
|---|---|
| 16 B | Small structs, list nodes |
| 32 B | Capability entries |
| 64 B | IPC message headers |
| 128 B | Task control blocks |
| 256 B | VMA entries |
| 512 B | Pathname buffers |
| 1024 B | Syscall argument buffers |
| 2048 B | Large temporary buffers |
Allocations > 2048 bytes go directly to the buddy allocator via vmalloc.
Copy-on-Write (COW)
COW enables efficient fork() by sharing physical frames between parent and child.
sequenceDiagram
participant P as Parent
participant K as Kernel
participant C as Child
P->>K: fork()
K->>K: Clone page tables (mark all RO + COW)
K->>K: Share physical frames (refcount++)
K-->>C: New address space
Note over C: Child writes to shared page
C->>K: Page fault (COW)
K->>K: Allocate new frame, copy page
K->>K: Remap child's PTE (RW, clear COW)
K->>K: Decrement parent refcount
K-->>C: Write succeeds
COW refcount invariant:
- refcount == 1 → sole owner (no sharing)
- refcount > 1 → shared (writes trigger COW fault)
- refcount == REFCOUNT_UNUSED → free-list frame
Page tables
The kernel uses x86_64 4-level paging (PML4 → PDPT → PD → PT).
| Level | Covers | Entry size |
|---|---|---|
| PML4 | 512 GiB | 8 bytes |
| PDPT | 1 GiB | 8 bytes |
| PD | 2 MiB (huge) | 8 bytes |
| PT | 4 KiB | 8 bytes |
Address space layout:
PML4[0..256]→ user space (per-process)PML4[256..512]→ kernel space (shared across all processes)
Each user process gets a fresh PML4 with the kernel half cloned from the boot PML4. This shares kernel L3/L2/L1 subtrees : kernel mapping changes propagate automatically.
Vmalloc
Large non-contiguous allocations use vmalloc, which maps arbitrary physical pages into a contiguous virtual range. Used for:
- Large metadata arrays
- Buffers that don't need physical contiguity
- Allocations > buddy max order
Boot Sequence
Strat9 OS boots on x86_64 via the UEFI bootloader. The boot flow transitions from 16-bit real mode through protected mode to 64-bit long mode before jumping into Rust.
Boot flow
flowchart TD
A[BIOS/UEFI] --> B[MBR / Stage 1]
B --> C[Stage 2]
C --> D[UEFI Bootloader]
D --> E[kstart - Assembly]
E --> F[start - Rust init]
F --> G[main.rs - Kernel main]
style A fill:#333,color:#fff
style D fill:#1a6b3a,color:#fff
style G fill:#1a6b3a,color:#fff
Stage 1 : MBR bootloader (bootloader/asm/)
The bootloader is written in NASM assembly. Stage 1 fits in exactly 512 bytes (MBR).
Responsibilities:
- Load Stage 2 from disk (LBA reads via BIOS INT 13h)
- Enable A20 line
- Enter protected mode (32-bit)
- Jump to Stage 2
Stage 2 : Protected → Long mode
Responsibilities:
- Detect available memory (INT 15h, E820)
- Load the kernel binary from disk
- Set up initial page tables (identity mapping + higher-half)
- Enable PAE, PGE, long mode
- Jump to 64-bit code
UEFI bootloader handoff
The kernel is loaded by the UEFI bootloader. The bootloader provides:
- Memory map (E820 equivalent)
- HHDM (Higher Half Direct Map) base address
- PML4 physical address
- RSDP (ACPI tables)
- Framebuffer info
The KernelArgs struct captures all handoff data:
KernelArgs {
hhdm_offset: u64, // Higher-half direct map base
pml4_physical: u64, // Boot page table
acpi_rsdp: Option<NonNull<u8>>, // ACPI RSDP
memory_regions: &[MemoryRegion], // E820 memory map
framebuffer: FramebufferInfo, // VGA/framebuffer
}
Kernel entry : kstart (assembly)
Located in boot/boot64.S:
- Verify BSS is zero, data is non-zero (sanity check)
- Set up kernel stack (128 KiB, from
STACKstatic) - Jump to
start()(Rust)
Kernel init : start() (Rust)
Located in boot/boot.rs → main.rs:
flowchart TD
A[serial::init] --> B[gdt::init_bsp]
B --> C[idt::init_bsp]
C --> D[memory::init]
D --> E[paging::init]
E --> F[interrupt::syscall::init]
F --> G[allocator::init - buddy + slab]
G --> H[acpi::init - parse MADT/IOAPIC]
H --> I[apic::init - Local APIC]
I --> J[smp::init - boot APs]
J --> K[timer::init - APIC timer]
K --> L[process::init - scheduler]
L --> M[shell::init - init process]
M --> N[scheduler::run - never returns]
Key init steps:
| Step | Module | What happens |
|---|---|---|
| Serial | serial.rs | Initialize COM1 (0x3F8) at 115200 baud |
| GDT | gdt.rs | Set up kernel/user code/data segments |
| IDT | idt.rs | Install interrupt handlers (timer, syscall, page fault) |
| Memory | memory/ | Parse E820, initialize buddy allocator |
| Paging | paging.rs | Set up kernel page tables (HHDM + higher-half) |
| Syscall | syscall.rs | Install syscall/sysret MSRs |
| Heap | heap.rs | Initialize slab allocator on top of buddy |
| ACPI | acpi/ | Parse MADT (APICs), IOAPIC, interrupt overrides |
| APIC | apic.rs | Initialize Local APIC (xAPIC or x2APIC) |
| SMP | smp.rs | INIT+SIPI sequence to boot Application Processors |
| Timer | timer.rs | Calibrate and start APIC timer (periodic) |
| Scheduler | scheduler/ | Create per-CPU run queues, spawn idle tasks |
| Init | shell/ | Spawn the first userspace process (init) |
SMP boot : Application Processors
sequenceDiagram
participant BSP
participant TRAMP as Trampoline (0x8000)
participant AP
BSP->>TRAMP: Copy trampoline to 0x8000
BSP->>TRAMP: Write CR3 + RSP to data area
BSP->>AP: INIT IPI (0x4500)
Note right of AP: 16-bit real mode
BSP->>AP: SIPI (vector=0x8 → 0x8000)
TRAMP->>AP: Enable protected mode
TRAMP->>AP: Enable long mode + paging
TRAMP->>AP: Load kernel stack
TRAMP->>AP: Jump to smp_main (Rust)
AP->>BSP: BOOTED_CORES++
BSP->>BSP: Wait for all APs
BSP->>AP: Open scheduler gate
AP->>AP: Start per-CPU scheduler
IPC Mechanisms
Strat9 OS provides a 3-level hybrid IPC transport model with a central Transport Manager that selects the appropriate isolation level per silo pair. Each level offers a different trade-off between performance and isolation.
IPC Architecture Overview
graph TB
subgraph "Transport Manager"
TM[Decision Matrix<br/>Tier × Tier → Level]
end
subgraph "N1 : TypeSafe IPC"
N1[IntrusiveMailbox<br/>Ring 0, approx. 3-10 cycles<br/>Rust type isolation]
end
subgraph "N2 : Lock-Free Ring"
N2[LockFreeRing<br/>Ring 3, approx. 400-4000 cycles<br/>SPSC + futex notification]
end
subgraph "N3 : MMU Thread Migration"
N3[MmuEndpoint<br/>Ring 3, approx. 800-2000 cycles<br/>CR3 switch + PCID]
end
TM --> N1
TM --> N2
TM --> N3
Transport selection matrix
| Source \ Dest | Critical | System | User |
|---|---|---|---|
| Critical | N1 (TypeSafe) | N1 (TypeSafe) | N2 (LockFree) |
| System | N1 (TypeSafe) | N2 (LockFree) | N2 (LockFree) |
| User | N2 (LockFree) | N2 (LockFree) | N3 (MMU) |
N1 : Type-Safe IPC (IntrusiveMailbox)
The fastest transport : kernel-internal, same address space, approx. 3-10 cycles per message.
How it works
Two kernel components (e.g., scheduler ↔ VFS) communicate via an intrusive LIFO mailbox. Messages are linked directly in kernel memory using tagged pointers (x86-64 ABA-safe). No copy, no lock, no syscall.
Constraints
- Ring 0 only : both sender and receiver must be kernel components
- 100% Rust Safe :
#[forbid(unsafe_code)]required,cargo-geiger = 0 - LIFO ordering : not FIFO; use N2 if ordering matters
- No untrusted input : forbidden for network, user data, external files
Usage
let mailbox = IntrusiveMailbox::new();
mailbox.push(b"notification")?; // approx. 3 cycles
let msg = mailbox.pop(); // LIFO: last message first
N2 : Lock-Free Ring (SPSC)
High-throughput shared-memory transport : approx. 400-4000 cycles depending on sleep mode.
How it works
A lock-free SPSC ring buffer backed by physically contiguous DMA-accessible pages. The producer writes data, sets a Release barrier on len, then publishes via tail.store(Release). The consumer observes tail, reads data via Acquire, and advances head. Futex notification for sleeping consumers.
Two sub-modes
| Mode | Latency | Use case |
|---|---|---|
| N2a (busy-poll) | approx. 400 cycles | Hot path, low-latency |
| N2b (futex sleep) | approx. 1000-4000 cycles | Background processing |
Memory layout
Page 0: RingHeader (cache-line padded)
Line 0: magic, capacity, slot_size, flags, notify_seq
Line 1: head (consumer hot) : 60B padding
Line 2: tail (producer hot) : 60B padding
Pages 1+: RingSlot entries
Each: [len:u16][flags:u16][data:u8; SLOT_SIZE]
Usage
let (producer, consumer) = create_spsc_pair(256); // 256 slots
// Producer (kernel or Ring 3)
producer.write(b"packet data")?;
producer.notify_consumer(); // futex wake
// Consumer (strate-net, Ring 3)
let mut buf = [0u8; 2048];
let n = consumer.read(&mut buf)?;
NIC integration (data plane)
NIC HW Queue 0 → Ring SPSC 0 → strate-net (poll round-robin)
NIC HW Queue 1 → Ring SPSC 1 →
NIC HW Queue 2 → Ring SPSC 2 →
Each RSS queue gets its own SPSC ring : no MPSC contention, no head-of-line blocking.
N3 : MMU Thread Migration (Research Track)
Maximum isolation : approx. 800-2000 cycles : currently research track with N2 fallback.
How it works
Inspired by L4 Thread Migration (Liedtke 1995): instead of a full syscall + context switch, the kernel migrates the CPU quantum directly to the target process by switching CR3 (address space) and jumping to the handler. Three tiers:
| Tier | Mechanism | Cost | Condition |
|---|---|---|---|
| N3a | Same-core handoff | approx. 200-400c | Same core, mappings valid |
| N3b | PCID-preserving CR3 | approx. 400-800c | PCID active, prefaulted |
| N3c | Full migration | approx. 800-2000c | First call, TLB flush |
Status
⚠️ Research track : N3 is not yet implemented. All User↔User pairs fall back to N2 (LockFree Ring).
Legacy mechanisms (still available)
These mechanisms predate the Transport Manager and remain functional:
IPC Ports (synchronous message-passing)
| Syscall | Description |
|---|---|
SYS_IPC_CREATE_PORT (200) | Create a new port |
SYS_IPC_SEND (201) | Send a message |
SYS_IPC_RECV (202) | Receive a message |
SYS_IPC_CALL (203) | Send and wait for reply |
SYS_IPC_REPLY (204) | Reply to a call |
SYS_IPC_BIND_PORT (205) | Bind to namespace |
SYS_IPC_UNBIND_PORT (206) | Unbind |
Typed MPMC Channels
let (tx, rx) = channel::<MyMessage>(64);
tx.send(msg)?; // blocks if full
let msg = rx.recv()?; // blocks if empty
Shared Ring (legacy)
High-throughput bulk IPC using shared-memory ring buffers. Superseded by N2 Lock-Free Ring for new code.
Semaphore
POSIX-like counting semaphore for synchronization.
Transport Manager API
Creating a transport
let manager = TransportManager::new();
let result = manager.establish(
src_silo, dst_silo,
TransportConfig {
min_level: TransportLevel::LockFree,
ring_capacity: Some(256),
slot_size: None,
},
)?;
// result.local and result.remote are the endpoints
Dynamic policy override
// Force N2 for a specific silo pair
manager.set_policy(src_sid, dst_sid, TransportLevel::LockFree, 512);
Syscall interface (planned)
| Syscall | # | Description |
|---|---|---|
SYS_TRANSPORT_CREATE | 240 | Create a transport |
SYS_TRANSPORT_SEND | 241 | Send a message |
SYS_TRANSPORT_RECV | 242 | Receive a message |
SYS_TRANSPORT_CLOSE | 243 | Close transport |
SYS_TRANSPORT_INFO | 244 | Get transport info |
Performance comparison
| Mechanism | Round-trip 64B | CPU usage/pkt | Isolation |
|---|---|---|---|
| Legacy IPC (syscall) | approx. 4000 cycles | approx. 2% | MMU |
| N1 TypeSafe | approx. 6-20 cycles | approx. 0.01% | Rust types |
| N2 LockFree (busy) | approx. 400-800 cycles | approx. 0.1% | MMU |
| N2 LockFree (futex) | approx. 1000-4000 cycles | approx. 0.2% | MMU |
| N3 MMU (research) | approx. 800-2000 cycles | approx. 0.5% | MMU (max) |
References
- Xu, P. & Roscoe, T. (2025) : The NIC should be part of the OS, HotOS'25 : arXiv
- Liedtke, J. (1995) : On µ-Kernel Construction, SOSP
- Hunt, G.C. & Larus, J.R. (2007) : Singularity: Rethinking the Software Stack, ACM Queue
- Levy, A. et al. (2017) : Multiprogramming a 64kB Computer Safely and Efficiently with Tock, SOSP
- Vyukov, D. : Bounded MPMC queue : lock-free SPSC/MPMC benchmarks
- Axboe, J. (2019) : Efficient IO with io_uring, kernel.org
IPC Transport Architecture
Driver Model
Strat9 OS uses a trait-based component model for drivers. Each driver registers itself during boot and is discovered through PCI/VirtIO enumeration.
Driver categories
graph TB
subgraph "Storage"
NVMe[NVMe]
AHCI[AHCI/SATA]
VIO_BLK[VirtIO Block]
end
subgraph "Network"
E1000[E1000/E1000e]
IGC[IGC]
VIO_NET[VirtIO Net]
end
subgraph "Input"
USB_HID[USB HID]
PS2[PS/2 Keyboard]
end
subgraph "Display"
FB[Framebuffer]
VGA[VGA text mode]
end
subgraph "Bus"
PCI[PCI enumeration]
VIO[VirtIO transport]
USB[XHCI host controller]
end
PCI --> NVMe
PCI --> AHCI
PCI --> E1000
PCI --> IGC
VIO --> VIO_BLK
VIO --> VIO_NET
USB --> USB_HID
Component trait
All drivers implement the Component trait, which provides a uniform registration interface:
pub trait Component: Send + Sync {
fn name(&self) -> &str;
fn init(&self) -> Result<(), ComponentError>;
fn shutdown(&self);
}
Components are registered at boot via the component-macro derive macro:
#[derive(Component)]
struct MyDriver { /* ... */ }
PCI enumeration
The PCI bus is scanned at boot using a BFS algorithm with early-exit optimization:
- For each (bus, device), probe function 0 first
- If vendor == 0xFFFF → skip all 8 functions (early exit)
- Read header type bit 7 for multi-function flag
- If PCI-to-PCI bridge → enqueue secondary bus
This reduces worst-case probes from 65,536 to approx. 8,192 for typical topologies.
Key PCI types:
| Type | Description |
|---|---|
PciAddress | Bus/device/function address |
PciDevice | Device info (vendor, class, BARs, IRQ) |
ProbeCriteria | Filter for device discovery |
NIC drivers
E1000 / E1000e
Intel Ethernet drivers supporting:
- Legacy descriptor rings (T/R)
- MSI-X interrupt moderation
- Multicast filter
- VLAN offload
IGC
Intel I225/I226 2.5GbE driver with:
- Advanced RX/TX descriptors
- Time-based interrupt coalescing
- Hardware timestamping
Common NIC infrastructure
| Module | Purpose |
|---|---|
nic-queues | TX/RX queue management, descriptor ring abstraction |
nic-buffers | Buffer allocation, DMA-safe memory |
net-core | Packet parsing, protocol headers |
driver-net-proto | Protocol driver trait |
Storage drivers
NVMe
Full NVMe driver with:
- I/O queues (per-CPU submission/completion pairs)
- MSI-X interrupt steering
- Namespace management
- Admin queue for controller commands
AHCI / SATA
AHCI controller driver for SATA devices:
- Port enumeration
- DMA PRDT (Physical Region Descriptor Table)
- FIS-based communication
VirtIO Block
Paravirtualized block device for QEMU/KVM:
- VirtIO queue negotiation
- Multi-queue support
- Feature bits (discard, write cache)
USB stack
graph TD
XHCI[XHCI Host Controller] --> HUB[Hub Driver]
HUB --> HID[USB HID - Keyboard/Mouse]
HUB --> MASS[USB Mass Storage]
HUB --> NET[USB Ethernet]
The XHCI driver manages the USB host controller, enumerates devices through hub traversal, and dispatches to class-specific drivers (HID, mass storage, etc.).
VirtIO transport
VirtIO provides paravirtualized device access for QEMU/KVM:
| Device | Module | Purpose |
|---|---|---|
| VirtIO Block | virtio/block | Block I/O |
| VirtIO Net | virtio/net | Network |
| VirtIO Console | virtio/console | Serial console |
| VirtIO GPU | virtio/gpu | Display |
| VirtIO Input | virtio/input | Keyboard/mouse |
Device discovery flow
sequenceDiagram
participant BOOT as Boot
participant PCI as PCI Scanner
participant DRV as Driver Registry
participant DEV as Device
BOOT->>PCI: Scan PCI bus
PCI->>DRV: Notify: device found
DRV->>DRV: Match vendor/device ID
DRV->>DEV: Initialize driver
DEV->>DEV: Map BARs, enable bus mastering
DEV->>DEV: Register IRQ handler
DEV-->>BOOT: Device ready
ABI Overview
The canonical ABI definitions live in workspace/abi (strat9-abi crate).
Focus modules:
strat9_abi::syscallfor syscall numbersstrat9_abi::datafor shared wire/data structsstrat9_abi::flagfor ABI-level flagsstrat9_abi::errnofor error codesstrat9_abi::bootfor boot handoff ABI
API reference:
ABI Changelog
This page tracks ABI evolution for strat9-abi.
Versioning policy
- Major (
ABI_VERSION_MAJOR) changes only for incompatible wire/layout changes. - Minor (
ABI_VERSION_MINOR) changes for backward-compatible additions. - No silent renumbering of existing syscall IDs.
repr(C)and explicit size checks are mandatory for exported ABI structs.
Current version
ABI_VERSION_MAJOR = 0ABI_VERSION_MINOR = 1- Packed:
0.1
See:
Recent ABI updates (auto-generated)
- 2026-09-27
f64aff3cImplement console rendering and mouse input handling tasks - 2026-09-13
3898c507feat(bootloader, kernel, abi): homemade strat9 UEFI bootloader : rewrite with scheduler and memory hardening - 2026-08-28
c03feb24fix(abi): ENOTSUP 52→95, XFS EiB constant fix, decode_fixed roundtrip tests - 2026-08-27
3ca6d7c6feat: Add U-Boot setup script and boot protocol implementation - 2026-08-25
88c52834refactor(arch): unify x86_64 crate access via x86_crate_shim, complete riscv stubs - 2026-08-25
c8ca94c4refactor(arch): route shared code through arch::xshim, gate limine/x86 deps - 2026-08-25
f3366e1drefactor(arch): migrate process/ and syscall/ call-sites to arch facade - 2026-08-25
4399b5f5test: pass 2 over full codebase — syscall, ABI data/handshake, NIC drivers, allocator - 2026-08-25
88feb023test(abi): exhaustive L0/L1 anti-regression suite - 2026-08-27
09216d7cMerge branch 'fix/elf-loader-hardening' into 'main' - 2026-08-27
2bad5b9bfeat(elf): loader hardening (#62 #63 #64 #67 #68, groundwork #66) - 2026-08-25
6ca7fef9fix(ipc): security hardening, perf fixes and typed wire-format layer - 2026-07-18
abb7dd7efeat(buddy): harden, optimize, and add kernel.toml configuration - 2026-07-17
fe68f9aafix(vfs,console,scheduler): VFS improvements, console perf, panic backtrace - 2026-07-14
3a5eb69dfeat(silo): replace Vec with heapless::Vec for bounded fields - 2026-07-09
ab22b0d9feat: 3-level IPC transport, N3 MMU thread migration, graphical silo, NIC/E1000 fixes - 2026-06-25
5a3d69c4feat: migrate input system to userspace - 2026-06-24
3fea3f37SMP hardware debug, NVMe fixes, VGA refactor, telnetd hardening, docs - 2026-06-16
e4ab1a96Enhance network stack, NIC drivers, USB/NVMe subsystems, and thermal management - 2026-06-03
0c400585Refactor the network stack and add dual-stack userspace tooling - 2026-05-24
aeade5efCode cleanup - 2026-05-24
b2bbcc0bImprove and fix IPC - 2026-05-23
db23b536Fix major memory leak and remove sshd silo - 2026-05-19
93c89275async: implement and optimize async I/O completion handling - 2026-05-18
7c5efb4dasync: io_uring-like async I/O — ring, dispatch, AHCI bridge (Phases 1-4) - 2026-05-14
cc4944a2Refactor and clean up code in various modules - 2026-05-14
ecc416dfrefactor: strate-init refactoring, ELF/VFS/signal hardening, TSC calibration fix - 2026-05-08
f070d41cAdd kernel entropy pool with interrupt-driven collection - 2026-05-08
9dedb9f1Implement robust list support (set_robust_list/get_robust_list) - 2026-05-08
d2e90a2fBridge clone() thread creation via SYS_THREAD_CREATE, add faccessat routing
Changelog entries
0.1
- Introduced canonical
strat9-abicrate as single source of truth. - Unified syscall numbers in
strat9_abi::syscall. - Unified shared structs (
TimeSpec,IpcMessage,FileStat, PCI types). - Added boot handoff ABI (
KernelArgs,MemoryRegion,MemoryKind) with magic/version checks. - Added ABI introspection syscall
SYS_ABI_VERSION.
Entry template
Use this template for future ABI entries:
### X.Y
- Added:
- <new syscalls/types/flags>
- Changed (compatible):
- <field additions, new constants, optional semantics>
- Changed (breaking):
- <layout/numbering/semantic breaks>
- Migration notes:
- <what userspace/kernel must update>
ABI Support Matrix
Status of musl platform APIs on x86_64-unknown-strat9.
Legend: OK = implemented, Stub = returns ENOSYS, Partial = limited.
POSIX File I/O
| API | Status | Notes |
|---|---|---|
| open / openat | OK | Via SYS_OPEN |
| read | OK | Via SYS_READ |
| write | OK | Via SYS_WRITE |
| close | OK | Via SYS_CLOSE |
| lseek | OK | Via SYS_LSEEK |
| pread | OK | Via SYS_PREAD |
| pwrite | OK | Via SYS_PWRITE |
| fstat / fstatat | OK | Via SYS_FSTAT / SYS_STAT |
| dup / dup2 | OK | Via SYS_DUP / SYS_DUP2 |
| pipe / pipe2 | OK | Via SYS_PIPE |
| fcntl | OK | Via SYS_FCNTL |
| mkdir / mkdirat | OK | Via SYS_MKDIR |
| unlink | OK | Via SYS_UNLINK |
| rmdir | OK | Via SYS_RMDIR |
| rename / renameat | OK | Via SYS_RENAME |
| link | OK | Via SYS_LINK |
| symlink | OK | Via SYS_SYMLINK |
| readlink | OK | Via SYS_READLINK |
| chmod / fchmod | OK | Via SYS_CHMOD / SYS_FCHMOD |
| truncate / ftruncate | OK | Via SYS_TRUNCATE / SYS_FTRUNCATE |
| chdir / fchdir | OK | Via SYS_CHDIR / SYS_FCHDIR |
| getcwd | OK | Via SYS_GETCWD |
| getdents | OK | Via SYS_GETDENTS |
| access | OK | Open + close probe |
| umask | OK | Via SYS_UMASK |
| fsync / fdatasync | Stub | ENOSYS |
| flock | Stub | ENOSYS |
| chown / fchown / lchown | Stub | ENOSYS |
| statvfs / fstatvfs | Stub | ENOSYS |
| mknod / mknodat / mkfifoat | Stub | ENOSYS |
Process Management
| API | Status | Notes |
|---|---|---|
| exit | OK | Via SYS_PROC_EXIT |
| fork | OK | Via SYS_PROC_FORK |
| execve | OK | Via SYS_PROC_EXEC |
| waitpid | OK | Via SYS_PROC_WAITPID |
| getpid / getppid / gettid | OK | |
| setsid / setpgid / getpgid / getsid | OK | |
| sched_yield | OK | Via SYS_PROC_YIELD |
| nanosleep | OK | Via SYS_NANOSLEEP |
| clock_gettime | OK | Via SYS_CLOCK_GETTIME |
| brk | OK | Via SYS_BRK |
| mmap / munmap | OK | Via SYS_MMAP / SYS_MUNMAP |
| uname | OK | Via SYS_PROC_UNAME |
| getuid / geteuid / getgid / getegid | Partial | Returns 0 (no UID model) |
| mprotect / mlock / munlock | Stub | ENOSYS |
| getrandom | OK | Via SYS_GETRANDOM (601), supports GRND_NONBLOCK |
Signals
| API | Status | Notes |
|---|---|---|
| kill | OK | Via SYS_KILL |
| sigaction | OK | Via SYS_SIGACTION |
| sigprocmask | OK | Via SYS_SIGPROCMASK |
| sigsuspend | OK | Via SYS_SIGSUSPEND |
| sigtimedwait | OK | Via SYS_SIGTIMEDWAIT |
| getitimer / setitimer | OK | Via SYS_GETITIMER / SYS_SETITIMER |
| sigaltstack | OK | Via SYS_SIGALTSTACK |
Network / Sockets
| API | Status | Notes |
|---|---|---|
| socketpair (AF_UNIX) | Partial | Backed by pipe (unidirectional) |
| recvfrom / sendto | Partial | Delegates to read/write |
| socket / bind / listen / accept / connect | Stub | ENOSYS |
| setsockopt / getsockopt | Stub | ENOSYS |
| shutdown | Stub | ENOSYS |
Epoll
| API | Status | Notes |
|---|---|---|
| epoll_create1 | Partial | Backed by pipe fd |
| epoll_ctl | Stub | No-op |
| epoll_pwait | Partial | Sleeps, no real multiplexing |
Syscall Reference
Complete reference for all Strat9 OS syscalls. Syscalls are invoked via the syscall instruction (x86_64). Arguments are passed in registers; the return value is in RAX.
ABI convention: Success returns a non-negative value. Errors return a negative errno value (two's complement). Userspace checks if result > 0xFFFF_F000 to detect errors, then applies !result + 1 to get the errno number.
Constants
AT_* flags (for *at syscalls)
| Constant | Value | Description |
|---|---|---|
AT_FDCWD | -100 | Use the process's current working directory as the base directory |
AT_REMOVEDIR | 0x200 | Remove a directory (for SYS_UNLINKAT) |
AT_SYMLINK_NOFOLLOW | 0x100 | Do not follow symbolic links (for SYS_FSTATAT) |
AT_EMPTY_PATH | 0x1000 | Operate on the fd itself when path is empty |
Open flags
| Flag | Value | Description |
|---|---|---|
O_RDONLY | 0 | Open for reading |
O_WRONLY | 1 | Open for writing |
O_RDWR | 2 | Open for reading and writing |
O_CREAT | 0x40 | Create file if it does not exist |
O_EXCL | 0x800 | Fail if file already exists (with O_CREAT) |
O_TRUNC | 0x200 | Truncate file to zero length |
O_APPEND | 0x400 | Append to end of file |
O_NONBLOCK | 0x800 | Non-blocking mode |
O_DIRECTORY | 0x10000 | Open as directory |
O_NOFOLLOW | 0x20000 | Do not follow symlinks |
Protection flags (mmap)
| Flag | Value | Description |
|---|---|---|
PROT_READ | 1 | Page can be read |
PROT_WRITE | 2 | Page can be written |
PROT_EXEC | 4 | Page can be executed |
Signal constants
| Constant | Value | Description |
|---|---|---|
SIG_DFL | 0 | Default signal handling |
SIG_IGN | 1 | Ignore signal |
SIG_BLOCK | 0 | Block signals in set |
SIG_UNBLOCK | 1 | Unblock signals in set |
SIG_SETMASK | 2 | Set signal mask to set |
Waitpid options
| Flag | Value | Description |
|---|---|---|
WNOHANG | 1 | Return immediately if no child has exited |
WUNTRACED | 2 | Also return for stopped children |
WCONTINUED | 4 | Also return for continued children |
Clock IDs
| Constant | Value | Description |
|---|---|---|
CLOCK_REALTIME | 0 | System-wide real-time clock |
CLOCK_MONOTONIC | 1 | Monotonic clock (not affected by adjustments) |
CLOCK_PROCESS_CPUTIME_ID | 2 | Per-process CPU time |
CLOCK_THREAD_CPUTIME_ID | 3 | Per-thread CPU time |
Handle operations
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 0 | SYS_NULL | : | 0 | No-op (used for benchmarking) |
| 1 | SYS_HANDLE_DUPLICATE | handle: u64 | new handle | Duplicate a capability handle |
| 2 | SYS_HANDLE_CLOSE | handle: u64 | 0 | Close a capability handle |
| 3 | SYS_HANDLE_WAIT | handle: u64, timeout_ns: u64 | 0 | Wait on a handle (blocks until ready or timeout) |
| 4 | SYS_HANDLE_GRANT | handle: u64, target_pid: u64 | 0 | Grant a capability to another process |
| 5 | SYS_HANDLE_REVOKE | handle: u64 | 0 | Revoke a capability (all holders lose access) |
| 6 | SYS_HANDLE_INFO | handle: u64, out_ptr: u64 | 0 | Query capability info (writes HandleInfo struct) |
Errors: EBADF (invalid handle), EPERM (no grant permission), ESRCH (target process not found)
Memory management
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 100 | SYS_MMAP | addr: u64, len: u64, prot: u64, flags: u64, fd: u64, offset: u64 | mapped address | Map a memory region |
| 101 | SYS_MUNMAP | addr: u64, len: u64 | 0 | Unmap a memory region |
| 102 | SYS_BRK | addr: u64 | new break | Set/clear the data break |
| 103 | SYS_MREMAP | old_addr: u64, old_len: u64, new_len: u64, flags: u64, new_addr: u64 | new address | Remap a memory region |
| 104 | SYS_MPROTECT | addr: u64, len: u64, prot: u64 | 0 | Change memory protection flags |
| 105 | SYS_MEM_REGION_EXPORT | addr: u64, len: u64 | region handle | Export a memory region as a shareable handle |
| 106 | SYS_MEM_REGION_MAP | region_handle: u64, addr: u64, len: u64 | mapped address | Map an exported memory region |
| 107 | SYS_MEM_REGION_INFO | region_handle: u64, out_ptr: u64 | 0 | Query region metadata |
Prot flags: PROT_READ (1), PROT_WRITE (2), PROT_EXEC (4)
Errors: EINVAL (bad alignment/flags), ENOMEM (out of memory), EACCES (permission denied), EEXIST (region already mapped)
IPC : Ports
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 200 | SYS_IPC_CREATE_PORT | : | port handle | Create a new IPC port |
| 201 | SYS_IPC_SEND | port_handle: u64, msg_ptr: u64, msg_len: u64 | 0 | Send a message to a port |
| 202 | SYS_IPC_RECV | port_handle: u64, buf_ptr: u64, buf_len: u64 | bytes received | Receive a message (blocks) |
| 203 | SYS_IPC_CALL | port_handle: u64, msg_ptr: u64, msg_len: u64 | bytes received | Synchronous RPC (send + wait for reply) |
| 204 | SYS_IPC_REPLY | msg_ptr: u64, msg_len: u64 | 0 | Reply to the current IPC call |
| 205 | SYS_IPC_BIND_PORT | port_handle: u64 | 0 | Bind a port as a listener |
| 206 | SYS_IPC_UNBIND_PORT | port_handle: u64 | 0 | Unbind a listening port |
| 207 | SYS_IPC_TRY_RECV | port_handle: u64, buf_ptr: u64, buf_len: u64 | bytes received (0 if empty) | Non-blocking receive |
| 208 | SYS_IPC_CONNECT | port_handle: u64 | 0 | Connect to a bound port |
| 210 | SYS_IPC_RING_CREATE | size_log2: u64 | ring handle | Create a shared ring buffer |
| 211 | SYS_IPC_RING_MAP | ring_handle: u64, addr: u64 | 0 | Map a shared ring into address space |
Errors: EBADF (invalid handle), ENOSPC (ring full), EAGAIN (non-blocking, nothing available), ETIMEDOUT (timeout exceeded)
IPC : Channels (typed MPMC)
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 220 | SYS_CHAN_CREATE | capacity: u64 | channel handle | Create a typed channel |
| 221 | SYS_CHAN_SEND | handle: u64, msg_ptr: u64 | 0 | Send a message (blocks if full) |
| 222 | SYS_CHAN_RECV | handle: u64, msg_ptr: u64 | 0 | Receive a message (blocks if empty) |
| 223 | SYS_CHAN_TRY_RECV | handle: u64, msg_ptr: u64 | 1 if received, 0 if empty | Non-blocking receive |
| 224 | SYS_CHAN_CLOSE | handle: u64 | 0 | Close channel handle |
Errors: EBADF (invalid handle), EPIPE (all endpoints disconnected), EAGAIN (try_recv on empty channel)
IPC : Semaphores
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 230 | SYS_SEM_CREATE | initial_value: u64 | semaphore handle | Create a counting semaphore |
| 231 | SYS_SEM_WAIT | handle: u64 | 0 | Decrement (blocks if zero) |
| 232 | SYS_SEM_TRYWAIT | handle: u64 | 1 if acquired, 0 if would block | Non-blocking decrement |
| 233 | SYS_SEM_POST | handle: u64 | 0 | Increment (wake a waiter) |
| 234 | SYS_SEM_CLOSE | handle: u64 | 0 | Close semaphore handle |
Errors: EBADF (invalid handle), EAGAIN (try_wait on zero semaphore)
PCI
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 240 | SYS_PCI_ENUM | criteria_ptr: u64, out_ptr: u64, max_count: u64 | device count | Enumerate PCI devices matching criteria |
| 241 | SYS_PCI_CFG_READ | addr_ptr: u64, offset: u64, width: u64 | config value | Read PCI configuration register |
| 242 | SYS_PCI_CFG_WRITE | addr_ptr: u64, offset: u64, width: u64, value: u64 | 0 | Write PCI configuration register |
Errors: EINVAL (invalid width/offset), EACCES (no PCI capability)
Async I/O
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 250 | SYS_ASYNC_SETUP | handle: u64, event_mask: u64 | async context | Set up async notification on a handle |
| 251 | SYS_ASYNC_ENTER | ctx: u64 | 0 | Enter async wait (yields until event) |
| 252 | SYS_ASYNC_CANCEL | ctx: u64 | 0 | Cancel pending async wait |
| 253 | SYS_ASYNC_MAP | ctx: u64, ring_handle: u64 | 0 | Map an event ring to the async context |
| 254 | SYS_ASYNC_DESTROY | ctx: u64 | 0 | Destroy async context |
Process management
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 300 | SYS_PROC_EXIT | exit_code: u64 | : (never returns) | Terminate current process |
| 301 | SYS_PROC_YIELD | : | 0 | Yield CPU to scheduler |
| 302 | SYS_PROC_FORK | frame: &SyscallFrame | child PID (parent), 0 (child) | Fork the current process (COW) |
| 308 | SYS_PROC_GETPID | : | process ID | Get current process ID |
| 309 | SYS_PROC_GETPPID | : | parent PID | Get parent process ID |
| 310 | SYS_PROC_WAITPID | pid: i64, status_ptr: u64, options: u64 | child PID | Wait for a child process |
| 311 | SYS_GETPID | : | process ID | Alias for SYS_PROC_GETPID |
| 312 | SYS_GETTID | : | thread ID | Get current thread ID |
| 314 | SYS_PROC_WAIT | : | : | Wait for any child |
| 315 | SYS_PROC_EXECVE | path_ptr: u64, path_len: u64, argv_ptr: u64, envp_ptr: u64 | : (replaces image) | Execute a new program |
| 341 | SYS_THREAD_CREATE | entry: u64, stack: u64, arg: u64 | thread ID | Create a new thread |
| 342 | SYS_THREAD_JOIN | tid: u64, status_ptr: u64 | 0 | Wait for thread to exit |
| 343 | SYS_THREAD_EXIT | status: u64 | : (never returns) | Terminate current thread |
Errors: ECHILD (no child processes), EAGAIN (thread creation failed), ENOMEM (out of memory)
Futex
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 303 | SYS_FUTEX_WAIT | addr: u64, val: u32, timeout_ns: u64 | 0 | Sleep if *addr == val |
| 304 | SYS_FUTEX_WAKE | addr: u64, max_wake: u32 | woken count | Wake up to N waiters |
| 305 | SYS_FUTEX_REQUEUE | addr: u64, max_wake: u32, addr2: u64, max_requeue: u32 | woken count | Wake + requeue to addr2 |
| 306 | SYS_FUTEX_CMP_REQUEUE | addr: u64, max_wake: u32, addr2: u64, max_requeue: u32, cmp_val: u32 | woken count | Conditional requeue |
| 307 | SYS_FUTEX_WAKE_OP | addr: u64, max_wake: u32, addr2: u64, max_requeue: u32, wake_op: u32 | woken count | Atomic op + wake |
Errors: EAGAIN (value mismatch in WAIT), ETIMEDOUT (timeout expired), EFAULT (invalid address)
Signals
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 320 | SYS_KILL | pid: i64, signum: u32 | 0 | Send signal to a process |
| 321 | SYS_SIGPROCMASK | how: i32, set_ptr: u64, oldset_ptr: u64 | 0 | Get/set signal mask |
| 322 | SYS_SIGACTION | signum: u64, act_ptr: u64, oact_ptr: u64 | 0 | Set signal handler |
| 323 | SYS_SIGALTSTACK | ss_ptr: u64, old_ss_ptr: u64 | 0 | Set alternate signal stack |
| 324 | SYS_SIGPENDING | set_ptr: u64 | 0 | Get pending signals |
| 325 | SYS_SIGSUSPEND | mask_ptr: u64 | : (restarted on signal) | Suspend until signal |
| 326 | SYS_SIGTIMEDWAIT | set_ptr: u64, info_ptr: u64, timeout_ptr: u64 | signal number | Wait for specific signal |
| 327 | SYS_SIGQUEUE | pid: i64, signum: u32, sigval_ptr: u64 | 0 | Queue a signal with data |
| 328 | SYS_KILLPG | pgrp: u64, signum: u32 | 0 | Send signal to process group |
| 352 | SYS_TGKILL | tgid: u64, tid: u64, signum: u32 | 0 | Send signal to specific thread |
| 353 | SYS_RT_SIGRETURN | : | : | Return from signal handler |
Process groups & sessions
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 316 | SYS_FCNTL | fd: u64, cmd: u64, arg: u64 | depends on cmd | File control operations |
| 317 | SYS_SETPGID | pid: u64, pgid: u64 | 0 | Set process group ID |
| 318 | SYS_GETPGID | pid: u64 | pgid | Get process group ID |
| 319 | SYS_SETSID | : | session ID | Create new session |
| 329 | SYS_GETITIMER | which: u64, out_ptr: u64 | 0 | Get interval timer |
| 330 | SYS_SETITIMER | which: u64, in_ptr: u64, out_ptr: u64 | 0 | Set interval timer |
| 331 | SYS_GETPGRP | : | pgrp | Get current process group |
| 332 | SYS_GETSID | pid: u64 | sid | Get session ID |
| 333 | SYS_SET_TID_ADDRESS | tidptr: u64 | 0 | Set clear-on-exit TID address |
| 334 | SYS_EXIT_GROUP | exit_code: u64 | : (never returns) | Exit all threads in process |
User/group IDs
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 335 | SYS_GETUID | : | uid | Get real user ID |
| 336 | SYS_GETEUID | : | euid | Get effective user ID |
| 337 | SYS_GETGID | : | gid | Get real group ID |
| 338 | SYS_GETEGID | : | egid | Get effective group ID |
| 339 | SYS_SETUID | uid: u64 | 0 | Set user ID |
| 340 | SYS_SETGID | gid: u64 | 0 | Set group ID |
Misc process
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 344 | SYS_UNAME | uts_ptr: u64 | 0 | Get system information (name, release, etc.) |
| 350 | SYS_ARCH_PRCTL | code: u64, addr: u64 | 0 | Architecture-specific process control |
File I/O
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 403 | SYS_OPEN | path_ptr: u64, path_len: u64, flags: u64 | file descriptor | Open a file |
| 404 | SYS_WRITE | fd: u64, buf_ptr: u64, buf_len: u64 | bytes written | Write to a file descriptor |
| 405 | SYS_READ | fd: u64, buf_ptr: u64, buf_len: u64 | bytes read | Read from a file descriptor |
| 406 | SYS_CLOSE | fd: u64 | 0 | Close a file descriptor |
| 407 | SYS_LSEEK | fd: u64, offset: u64, whence: u64 | new position | Seek in a file |
| 408 | SYS_FSTAT | fd: u64, stat_ptr: u64 | 0 | Get file status (fstat) |
| 409 | SYS_STAT | path_ptr: u64, path_len: u64, stat_ptr: u64 | 0 | Get file status by path |
| 413 | SYS_ACCESS | path_ptr: u64, path_len: u64, mode: u64 | 0 | Check file accessibility (uses effective UID/GID, not real). Prefer SYS_FACCESSAT for new code. |
| 430 | SYS_GETDENTS | fd: u64, buf_ptr: u64, buf_len: u64 | bytes read | Read directory entries |
| 431 | SYS_PIPE | fds_ptr: u64 | 0 | Create a pipe pair |
| 432 | SYS_DUP | old_fd: u64 | new fd | Duplicate file descriptor |
| 433 | SYS_DUP2 | old_fd: u64, new_fd: u64 | new fd | Duplicate to specific fd |
| 456 | SYS_PREAD | fd: u64, buf_ptr: u64, buf_len: u64, offset: u64 | bytes read | Pread at offset |
| 457 | SYS_PWRITE | fd: u64, buf_ptr: u64, buf_len: u64, offset: u64 | bytes written | Pwrite at offset |
Open flags: O_RDONLY (0), O_WRONLY (1), O_RDWR (2), O_CREAT (0x40), O_TRUNC (0x200), O_APPEND (0x400), O_EXCL (0x800)
Errors: ENOENT (file not found), EACCES (permission denied), EBADF (bad fd), ENOTDIR (not a directory), EISDIR (is a directory), ENOSPC (disk full), EIO (I/O error)
File system operations
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 440 | SYS_CHDIR | path_ptr: u64, path_len: u64 | 0 | Change working directory |
| 441 | SYS_FCHDIR | fd: u64 | 0 | Change working directory by fd |
| 442 | SYS_GETCWD | buf_ptr: u64, buf_len: u64 | bytes written | Get current working directory |
| 443 | SYS_IOCTL | fd: u64, cmd: u64, arg: u64 | depends on cmd | Device I/O control |
| 444 | SYS_UMASK | mask: u64 | old mask | Set file mode creation mask |
| 445 | SYS_UNLINK | path_ptr: u64, path_len: u64 | 0 | Delete a file |
| 446 | SYS_RMDIR | path_ptr: u64, path_len: u64 | 0 | Remove a directory |
| 447 | SYS_MKDIR | path_ptr: u64, path_len: u64, mode: u64 | 0 | Create a directory |
| 448 | SYS_RENAME | old_ptr: u64, old_len: u64, new_ptr: u64, new_len: u64 | 0 | Rename a file |
| 449 | SYS_LINK | old_ptr: u64, old_len: u64, new_ptr: u64, new_len: u64 | 0 | Create a hard link |
| 450 | SYS_SYMLINK | target_ptr: u64, target_len: u64, link_ptr: u64, link_len: u64 | 0 | Create a symbolic link |
| 451 | SYS_READLINK | path_ptr: u64, path_len: u64, buf_ptr: u64, buf_len: u64 | bytes read | Read symbolic link target |
| 452 | SYS_CHMOD | path_ptr: u64, path_len: u64, mode: u64 | 0 | Change file permissions |
| 453 | SYS_FCHMOD | fd: u64, mode: u64 | 0 | Change file permissions by fd |
| 454 | SYS_TRUNCATE | path_ptr: u64, path_len: u64, len: u64 | 0 | Truncate a file |
| 455 | SYS_FTRUNCATE | fd: u64, len: u64 | 0 | Truncate a file by fd |
*at variants (relative to directory fd)
These syscalls resolve paths relative to a directory file descriptor instead of the process CWD. They are the POSIX-standard way to open, stat, and manipulate files safely in multi-threaded programs.
Special dirfd values
| Value | Constant | Meaning |
|---|---|---|
-100 | AT_FDCWD | Use the process's current working directory (CWD) as the base |
≥ 0 | valid fd | Use the opened directory referenced by this file descriptor |
Syscall reference
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 462 | SYS_OPENAT | dirfd: u64, path_ptr: u64, path_len: u64, flags: u64 | file descriptor | Open a file relative to dirfd. If path_ptr is absolute, dirfd is ignored. |
| 463 | SYS_FSTATAT | dirfd: u64, path_ptr: u64, path_len: u64, stat_ptr: u64, flags: u64 | 0 | Get file status relative to dirfd. stat_ptr receives a FileStat struct. |
| 464 | SYS_UNLINKAT | dirfd: u64, path_ptr: u64, path_len: u64, flags: u64 | 0 | Delete a file relative to dirfd. If AT_REMOVEDIR flag is set, removes a directory. |
| 465 | SYS_RENAMEAT | olddirfd: u64, old_ptr: u64, old_len: u64, newdirfd: u64, new_ptr: u64, new_len: u64 | 0 | Rename/move a file. Source and destination can have different base directories. |
| 466 | SYS_MKDIRAT | dirfd: u64, path_ptr: u64, path_len: u64, mode: u64 | 0 | Create a directory relative to dirfd. |
| 467 | SYS_READLINKAT | dirfd: u64, path_ptr: u64, path_len: u64, buf_ptr: u64, buf_len: u64 | bytes read | Read the target of a symbolic link relative to dirfd. |
| 468 | SYS_FACCESSAT | dirfd: u64, path_ptr: u64, path_len: u64, mode: u64, flags: u64 | 0 | Check file accessibility. mode: R_OK (4), W_OK (2), X_OK (1), F_OK (0). |
Flags
| Flag | Value | Description |
|---|---|---|
AT_FDCWD | -100 | Use process CWD as base directory |
AT_REMOVEDIR | 0x200 | Unlink a directory instead of a file (for SYS_UNLINKAT) |
AT_SYMLINK_NOFOLLOW | 0x100 | Do not follow symlinks (for SYS_FSTATAT) |
AT_EMPTY_PATH | 0x1000 | Operate on dirfd itself when path is empty |
Errors
EBADF (invalid dirfd), ENOENT (path not found), EACCES (permission denied), ENOTDIR (dirfd is not a directory), EEXIST (file exists for CREATE_EXCL), EINVAL (invalid flags)
Poll / I/O multiplexing
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 460 | SYS_POLL | fds_ptr: u64, nfds: u64, timeout_ms: i64 | ready count | Poll file descriptors |
| 461 | SYS_PPOLL | fds_ptr: u64, nfds: u64, timeout_ptr: u64, sigmask_ptr: u64 | ready count | Ppoll with signal mask |
Network
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 410 | SYS_NET_RECV | buf_ptr: u64, buf_len: u64 | bytes received | Receive network packet |
| 411 | SYS_NET_SEND | buf_ptr: u64, buf_len: u64 | bytes sent | Send network packet |
| 412 | SYS_NET_INFO | info_type: u64, buf_ptr: u64 | 0 | Query network info (IP, gateway, etc.) |
Volume (block device)
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 420 | SYS_VOLUME_READ | handle: u64, offset: u64, buf_ptr: u64, buf_len: u64 | bytes read | Read from volume |
| 421 | SYS_VOLUME_WRITE | handle: u64, offset: u64, buf_ptr: u64, buf_len: u64 | bytes written | Write to volume |
| 422 | SYS_VOLUME_INFO | handle: u64, out_ptr: u64 | 0 | Query volume info |
Time
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 500 | SYS_CLOCK_GETTIME | clock_id: u64, tp_ptr: u64 | 0 | Get clock time |
| 501 | SYS_NANOSLEEP | req_ptr: u64, rem_ptr: u64 | 0 | Sleep for a duration |
| 502 | SYS_CLOCK_NANOSLEEP | clock_id: u64, flags: u64, req_ptr: u64, rem_ptr: u64 | 0 | Sleep on a specific clock |
Clock IDs: CLOCK_REALTIME (0), CLOCK_MONOTONIC (1), CLOCK_PROCESS_CPUTIME_ID (2), CLOCK_THREAD_CPUTIME_ID (3)
Debug & miscellaneous
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 600 | SYS_DEBUG_LOG | msg_ptr: u64, msg_len: u64 | 0 | Write a debug message to kernel log |
| 601 | SYS_GETRANDOM | buf: u64, len: usize, flags: u32 | bytes written | Fill buffer with random bytes |
| 610 | SYS_SET_ROBUST_LIST | head: u64, len: usize | 0 | Set robust futex list head |
| 611 | SYS_GET_ROBUST_LIST | pid: i64, head_ptr: u64, len_ptr: u64 | 0 | Get robust futex list head |
Module management
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 700 | SYS_MODULE_LOAD | path_ptr: u64, path_len: u64 | module ID | Load a kernel module |
| 701 | SYS_MODULE_UNLOAD | module_id: u64 | 0 | Unload a kernel module |
| 702 | SYS_MODULE_GET_SYMBOL | module_id: u64, name_ptr: u64, name_len: u64 | symbol address | Look up a symbol in a loaded module |
| 703 | SYS_MODULE_QUERY | out_ptr: u64, max_count: u64 | module count | List loaded modules |
Silo management
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 800 | SYS_SILO_CREATE | : | silo ID | Create a new silo |
| 801 | SYS_SILO_CONFIG | silo_id: u64, key_ptr: u64, key_len: u64, val_ptr: u64, val_len: u64 | 0 | Configure a silo |
| 802 | SYS_SILO_ATTACH_MODULE | silo_id: u64, module_id: u64 | 0 | Attach a module to a silo |
| 803 | SYS_SILO_START | silo_id: u64 | 0 | Start a silo |
| 804 | SYS_SILO_STOP | silo_id: u64 | 0 | Stop a silo |
| 805 | SYS_SILO_KILL | silo_id: u64 | 0 | Kill a silo (force stop) |
| 806 | SYS_SILO_EVENT_NEXT | silo_id: u64, out_ptr: u64 | 0 | Wait for next silo event |
| 807 | SYS_SILO_SUSPEND | silo_id: u64 | 0 | Suspend a silo |
| 808 | SYS_SILO_RESUME | silo_id: u64 | 0 | Resume a silo |
| 809 | SYS_SILO_PLEDGE | promises_ptr: u64, promises_len: u64 | 0 | Restrict syscalls (pledge) |
| 810 | SYS_SILO_UNVEIL | path_ptr: u64, path_len: u64, perms_ptr: u64, perms_len: u64 | 0 | Restrict filesystem access (unveil) |
| 811 | SYS_SILO_ENTER_SANDBOX | : | 0 | Enter sandbox mode (irreversible) |
| 812 | SYS_SILO_RENAME | silo_id: u64, name_ptr: u64, name_len: u64 | 0 | Rename a silo |
ABI version
| # | Syscall | Parameters | Return | Description |
|---|---|---|---|---|
| 900 | SYS_ABI_VERSION | : | (major << 16) | minor | Query ABI version |
Common errno values
| Value | Name | Description |
|---|---|---|
| 1 | EPERM | Operation not permitted |
| 2 | ENOENT | No such file or directory |
| 3 | ESRCH | No such process |
| 4 | EINTR | Interrupted system call |
| 5 | EIO | Input/output error |
| 7 | E2BIG | Argument list too long |
| 9 | EBADF | Bad file descriptor |
| 10 | ECHILD | No child processes |
| 11 | EAGAIN | Resource temporarily unavailable |
| 12 | ENOMEM | Out of memory |
| 13 | EACCES | Permission denied |
| 14 | EFAULT | Bad address |
| 17 | EEXIST | File exists |
| 20 | ENOTDIR | Not a directory |
| 21 | EISDIR | Is a directory |
| 22 | EINVAL | Invalid argument |
| 28 | ENOSPC | No space left on device |
| 32 | EPIPE | Broken pipe |
| 38 | ENOSYS | Function not implemented |
| 52 | ENOTSUP | Not supported |
| 98 | EADDRINUSE | Address already in use |
| 110 | ETIMEDOUT | Connection timed out |
| 111 | ECONNREFUSED | Connection refused |
Syscall Layer
The userspace syscall crate is workspace/components/syscall (strat9-syscall).
Useful entry points:
strat9_syscall::callfor high-level wrappersstrat9_syscall::numberfor syscall constantsstrat9_syscall::errorfor userspace error mappingstrat9_syscall::datafor shared data structs
API reference:
Changelog
Project stats
- Total commits:
662 - Latest tag:
0.1.0 - Repository: git.strat9-os.org
Recent commits (auto-generated)
- 2026-09-27
f64aff3Implement console rendering and mouse input handling tasks - 2026-09-23
e53faf1docs: update published documentation - 2026-09-23
dec6766Merge branch 'ELF-tuning-and-fix' into 'main' - 2026-09-23
e724272Fix : unify logging to log::*, reject null ET_EXEC entry, warn on zero-size COPY relocs - 2026-09-14
4c008c5docs: update published documentation - 2026-09-13
981429bMerge branch 'fix/UEFI-bootloader' into 'main' - 2026-09-13
3898c50feat(bootloader, kernel, abi): homemade strat9 UEFI bootloader : rewrite with scheduler and memory hardening - 2026-08-28
228eaa5fix: regenerate corrupted Cargo.lock (stray merge conflict markers from rebase) - 2026-08-28
93dd0e5kernel: integrate cpuid.rs leaf1 detection diagnostics from ramfs-hardening WIP - 2026-08-28
4eb5b3dMerge branch 'enhance-async-implementation' into 'main' - 2026-08-28
00d83d7feat(kernel): async I/O overhaul : userspace ring mapping, deferred completions, Scheme trait redesign - 2026-08-28
6738421Merge branch 'fix/errno-and-capabilities' into 'main' - 2026-08-28
c03feb2fix(abi): ENOTSUP 52→95, XFS EiB constant fix, decode_fixed roundtrip tests - 2026-08-27
50a807dfix(framebuffer): update fill and blit operations to use dynamic detection - 2026-08-27
7807362Merge branch 'feat/graphics-performance' into 'main' - 2026-08-27
7ed9263style: apply rustfmt to paging.rs and framebuffer.rs - 2026-08-24
aaf0fb6fix(kernel): work around nightly LLVM ISel abort on pshufb/movntdq - 2026-08-24
7087cffrefactor(graphics): CanvasBuffer hygiene - S4/S5/S6 partial - 2026-08-24
0e0d5feperf(graphics): streaming stores for large fills/blits (S2) - 2026-08-24
4aec188perf(graphics): Write-Combining framebuffer via dedicated PAT entry; unified present throttling - 2026-08-23
6b3ceccperf(graphics): batched blit API and lockless framebuffer info getters - 2026-08-23
75af9adperf(graphics): zero-copy VirtIO GPU presentation with coalesced damage - 2026-08-23
706577fperf(graphics): SIMD pixel ingest + explicit-width wire protocol for /dev/display - 2026-08-23
d4f597dperf(graphics): event-driven compositor with partial damage present - 2026-08-27
067772eMerge branch 'feat/Replace-Limine-bootloader-with-U-Boot-for-multi-architecture-support' into 'main' - 2026-08-27
3ca6d7cfeat: Add U-Boot setup script and boot protocol implementation - 2026-08-27
df64d6dMerge branch 'fixing-the-network-again' into 'main' - 2026-08-27
eccb4d1fix(net): ICMP reply handling + wasmi upgrade + clippy fixes - 2026-08-27
25cc18bMerge branch 'feat/hal-arch-facade' into 'main' - 2026-08-26
e725386ok - 2026-08-26
7262385fix(arch,riscv64): dedupe duplicated stub modules in facade_riscv - 2026-08-26
892cb03fix(arch,riscv64): SegmentSelector newtype for gdt stub, safe unmap on stub mapper - 2026-08-26
580b86ffeat(ostd): Add/Suboperator impls for neutral PhysAddr/VirtAddr - 2026-08-26
4260fbcfix(arch,riscv64): mapper::MapToError path fix inside OffsetPageTable impl - 2026-08-26
60b50c6feat(arch,riscv64): working Sv48 translate walker in stub mapper - 2026-08-26
529715afeat(ipc,riscv64): Sv48 tagged-pointer layout for IntrusiveMailbox - 2026-08-26
c086395feat(arch,riscv64): PciDevice config-space accessors (stubbed, R5 wires ECAM) - 2026-08-26
a790379fix(arch,riscv64): MouseEvent struct + u8 KEY_* scancode parity with x86 - 2026-08-26
3e447a2fix(arch,riscv64): restore unmap (tuple PhysFrame+MapperFlush) on stub mapper - 2026-08-26
bf02f88fix(arch,riscv64): unmap returns PhysFrame, Cr3::write/Cr3Flags::empty stubs - 2026-08-26
dc404b5fix(arch,riscv64): sealed PortValue trait for port stub (kills E0034) - 2026-08-26
6d3f772fix(arch,riscv64): port stub typed read/write impls, drop stale traits - 2026-08-26
c9dbfb0fix(arch): annotate ambiguous PhysFrame::containing_address call-sites - 2026-08-26
8731a50refactor(arch): Port trait-based reads, gate top TUI behind x86_64 - 2026-08-25
1da8196fix(arch,riscv64): dedupe stubs, align keyboard read_char, FrameAllocator cfg - 2026-08-25
daf86f5fix(arch,riscv64): align stub signatures with x86 call-sites - 2026-08-25
922d429feat(arch,riscv64): full PciDevice struct + PCI surface stubs - 2026-08-25
acba1cffeat(arch,riscv64): Mapper trait surface on Sv48 skeleton - 2026-08-25
62d200efeat(arch,riscv64): real Sv48 scaffolding in x86_64-stub, ostd pointer helpers - 2026-08-25
e96df8frefactor(arch): gate VGA-UI commands behind x86_64, complete riscv framebuffer_info stub
Publishing
One-command publication script
At repository root:
./publish-doc.sh
This script:
- builds docs (
cargo make docs-site) - regenerates ABI changelog auto section from git history
- commits/pushes current branch changes
- uploads built website to the remote vhost via SSH
The docs builder publishes:
- mdBook pages under
docs-site/ - rustdoc for all workspace crates (
cargo doc --workspace --no-deps) - a combined static site in
build/docs-site
Architecture de tests Strat9-OS
Objectif : non-régression systématique pour chaque module du système — kernel, ABI, IPC, mémoire, VFS, drivers, composants userspace (Silos/Strates).
Statut initial constaté : ~66
#[test]dans tout le workspace, concentrés suripc/lockfree_ring,xfs-rsetfs-abstraction. Aucun stage de test en CI.cargo test --workspaceéchoue côté hôte (duplicate lang item panic_impl) car le kernel et les composants sont#![no_std]ciblantx86_64-unknown-none.
1. Les 5 couches de test (pyramide)
┌─────────────────────────────────────┐
L4 │ Tests end-to-end QEMU (ISO boot) │ lents, few, high value
├─────────────────────────────────────┤
L3 │ Self-tests kernel in-QEMU │ (feature `selftest`)
├─────────────────────────────────────┤
L2 │ Unit tests no_std on host │ pure logic, gated
├─────────────────────────────────────┤
L1 │ Unit tests host (std) │ ABI, parsers, codecs
├─────────────────────────────────────┤
L0 │ Static ABI conformance assertions │ compile-time
└─────────────────────────────────────┘
L0 — Conformance ABI statique (compile-time)
static_assertions::assert_eq_size!/assert_eq_align!/const_assert!sur toutes les structures#[repr(C)]traversant la frontière kernel ↔ userspace.- Golden values des numéros de syscall : tout changement est une rupture d'ABI
et doit être un commit explicite + bump de
ABI_VERSION_MINOR. - Fichier :
workspace/abi/tests/abi_stability.rs.
L1 — Tests unitaires hôte (std)
Tout ce qui est logique pure sans dépendance matérielle :
- crate
strat9-abientière (errno, flags, parsing IP, codec IPC, payloads) ; - math safe (
safe_math), unicode, parseurs TOML de boot ; - conversions POSIX ↔ Strat9 (musl-compat, relibc shims).
- Exécution :
cargo test -p <crate>— doit rester vert en permanence.
L2 — Tests unitaires no_std sur hôte
Pour la logique kernel testable sans matériel (allocateurs, anneaux IPC, tables de capabilities, routage de schemes) :
- Pattern recommandé : extraire la logique pure dans des modules sans dépendance
arch (
mod pure), tester ces modules avec#[cfg(test)]compilés pour hôte ; - Alternative : harness de test custom embarqué dans l'image (voir L3).
L2 — opérationnel : crate ombre workspace/kernel-l2-tests
Les modules purs du kernel sont compilés verbatim (#[path] includes)
dans une crate hôte qui fournit des fakes fonctionnels :
sync/irq (token factice), arch/x86_64/percpu (compteurs), silo,
translation HHDM (offset réel vers une arena hôte → l'allocateur buddy
travaille sur de la vraie mémoire). Les tests inline #[cfg(test)] du
kernel s'exécutent enfin. Cible : cargo make test-kernel-l2.
Modules couverts : sync/{fixed_queue,guardian,spinlock}, ipc/{message, lockfree_ring,mailbox}, memory/{boot_alloc,zone,frame,buddy},
namespace, boot/toml. Prochains candidats : ipc/channel, vfs
(routeur de schemes), classes d'ordonnancement.
L3 — Self-tests kernel in-QEMU (feature selftest)
Le kernel dispose déjà de cargo make kernel-test + selftest.rs.
Convention à généraliser :
- chaque sous-système déclare une fonction
<module>_selftest() -> Result; - l'orchestrateur affiche
[selftest] PASS/FAIL <name>sur le port série ; - sortie vérifiable mécaniquement : grep
SELFTEST-FAILsur la sortie série.
L4 — Tests d'intégration bout-en-bout QEMU
Silos utilisateurs dédiés (test_mem_region, test_exec, …) lancés depuis
l'initfs ; le harnais QEMU vérifie la sortie série (TEST-PASS/TEST-FAIL)
avec timeout. Cible cargo-make unique : cargo make ci-qemu-tests.
2. Matrice module → couches
| Module | L0 | L1 | L2 | L3 | L4 |
|---|---|---|---|---|---|
strat9-abi | ✅ | ✅ | — | — | — |
kernel/ipc (codec, rings, mailbox) | ✅ | ✅* | ✅ | ✅ | ✅ |
kernel/memory (buddy, heap, cow) | — | partiel | ✅ | ✅ | ✅ (test_mem_region) |
kernel/process (sched, signals, futex) | — | — | ✅ | ✅ | ✅ |
kernel/vfs (schemes, router, ramfs) | — | — | ✅ | ✅ | ✅ |
strate-fs-abstraction | — | ✅ | — | — | ✅ |
strate-fs-ext4/ramfs | — | partiel | — | — | ✅ |
| drivers (e1000, virtio, ahci) | — | partiel | — | ✅ | ✅ (loopback QEMU) |
* via extraction pure ou duplication contrôlée des constantes.
3. Conventions
- Nommage :
tests/<module>_*.rs(intégration),#[cfg(test)] mod tests(unitaires in-crate quand le crate compile en std pour les tests). - Un test = un comportement ; tables de cas (
for case in CASES) privilégiées pour les parseurs/codecs. - Golden values pour toute constante cross-binaire (numéros syscall, layouts, opcodes IPC) : la régression est détectée même si aucun consumer n'est compilé.
- Roundtrip systématique pour codecs :
decode(encode(x)) == x+ tests de corruption/troncature (bytes aléatoires déterministes, pas de RNG non seedé). - Chaque PR qui touche un module doit être accompagnée des tests de sa couche.
4. CI (roadmap)
Stage GitLab test entre build et release :
cargo test --workspace --exclude strat9-kernel ...(crates host-safe) ;- build kernel
--features selftest(garde la compilation des selftests) ; - job optionnel QEMU : boot ISO + grep série (runner avec qemu-system-x86_64).
5. Questions ouvertes (à arbitrer)
- Base de branche : branchée sur le HEAD de
fix/ipc-scheme-security-review(5078255). Si vous préférez une basemain, rebase trivial (fichiers nouveaux uniquement). - L2 kernel-hôte : préférer l'extraction de modules purs (refactor plus large, meilleure couverture) ou dupliquer les tests in-QEMU (zéro refactor, exécution plus lente) ? Recommandation : extraction progressive, en commençant par IPC.
- QEMU en CI : un runner GitLab avec KVM/qemu est-il disponible ? Sinon TCG (lent mais suffisant pour L3/L4 courts).
Rapport de tests — findings
Issues découvertes par la suite anti-régression (
test/anti-regression-suite). Chaque finding est référencé par un test qui le documente/pinne.
F1 — Build kernel cassé par le nightly non épinglé (bloquant) — ✅ RÉSOLU
Épinglé sur nightly-2026-08-20 (dernier bon vérifié : bisect 08-20 OK /
08-23 cassé, cause PR rust-lang/rust#160302 mergé le 23/08 à 21:33 UTC,
commit fb6531d550e0). Le code exige en outre Step::forward/backward_overflowing
(PR #155114, 09/07/2026) ⇒ fenêtre compatible [2026-07-10 .. 2026-08-22].
Correctif associé : [profile.dev] opt-level = 0 → 1 car O0 déclenche une
ICE LLVM distincte (#158532) sur les intrinsèques AVX2. Builds debug,
release, selftest et gate hôte tous vérifiés verts.
framebuffer/x86/avx2.rs utilise #[target_feature(enable = "avx2")].
Sur rustc 1.100.0-nightly, activer sse/avx sur une cible soft-float
(x86_64-unknown-none) est une erreur dure (lint x86_softfloat_sse).
cargo make kernel échoue sur toutes les branches.
Options :
- (recommandé, immédiat) Épingler l'outilchain :
channel = "nightly-2026-06-xx"dansrust-toolchain.toml— dernière version connue fonctionnelle ; - (long terme) Adapter
avx2.rs: runtime detection + assembly inline ou wrapper compilé sans soft-float constraint.
F2 — VfsTimestamp::to_filetime déborde — ⏳ à faire
(secs + WINDOWS_EPOCH_OFFSET) as u64 * 10_000_000 sans arithmetic checkée :
panic debug / wrap release au-delà de ~an 60000. Entrée contrôlée par le disque
ou le réseau (chemins stat) ⇒ à traiter comme entrée hostile.
Fix proposé : checked_mul → FsError::ArithmeticOverflow.
F3 — FsCapabilities::xfs()/btrfs() : valeurs PiB au lieu de EiB — ✅ RÉSOLU
XFS = 8 EiB réels ; btrfs clampé à u64::MAX (16 EiB = 2^64). Tests mis à jour.
Commentaires « 8 EiB »/« 16 EiB », chaînes littérales à 5 facteurs 1024
⇒ valeurs réelles 8 PiB / 16 PiB (16 EiB ne tient même pas dans u64).
Décider : corriger les commentaires ou l'arithmétique.
F4 — parse_ipv6_literal sur-accepte le :: — ⏳ à durcir (RFC 4291)
RFC 4291 interdit :: quand 8 groupes explicites sont déjà présents ;
le parseur accepte 1:2:3:4:5:6:7:8::. Sans danger immédiat (sortie bien
formée) mais à durcir pour la conformité.
F5 — OpenReply n'implémente pas KnownLayout — ✅ RÉSOLU
KnownLayout dérivé via zerocopy (déjà dépendance) sur toute la surface
de payloads repr(C) + InlineBlobHeader. Test de regression roundtrip
decode_fixed ajouté pour chaque struct fixe.
F6 — ENOTSUP = 52 n'est pas la valeur Linux — ✅ RÉSOLU
Renommé 52 → 95 (= Linux x86_64) : constante ABI, selftest kernel, tests golden, entrée changelog ABI.
F7 — Test unitaire préexistant cassé depuis longtemps
strate-fs-abstraction/tests/unit_tests.rs importait fs_abstraction
(ancien nom de crate) : aucun test de cette crate ne tournait depuis le
renommage. Corrigé — preuve que l'absence de CI de test laisse pourrir
la couverture silencieusement.
F8 — Constantes POSIX fausses dans strat9_syscall::flag — ✅ RÉSOLU
Trouvé lors de la passe 2 par la table dorée Linux :
O_NOFOLLOW = 0o100000→ en réalité O_CLOEXEC sur Linux ; corrigé à0o400000;O_NOCTTY = 0o400000→ en réalité O_NOFOLLOW sur Linux (!) ; corrigé à0o000400, détecté par le test doré lui-même après le premier fix ;O_DSYNC = 0o02000000→ O_CLOEXEC aussi ; corrigé à0o10000. La traductionposix_oflags_to_strat9(crate abi) utilisait ses propres copies correctes — mais tout composant consommant ces constantes directement cassait. Leçon : les tables dorées attrapent ce que la relecture manuelle rate.
F9 — Test inline lockfree_ring::full_ring faux depuis sa création — ✅ CORRIGÉ
Découvert dès la première exécution réelle des tests inline du kernel (jamais
possibles avant le harnais L2) : LockFreeRing::new(4, 64) crée 4 slots
(next_power_of_two), donc 4 writes réussissent — le test attendait Full
au 4ᵉ. Le test était off-by-one ; corrigé dans le bloc #[cfg(test)].
F10 — Port I/O out 0xe9 dans les primitives de sync — ✅ CONTOURNÉ
emit_trace_e9 (spinlock.rs) et d'autres traces diagnostiques émettent sur
le port série E9 via asm!("out 0xe9") : instruction ring-0, SIGSEGV garanti
en userspace. Compilé hors du binaire de test uniquement sous le cfg
kernel_l2_host (posé par workspace/kernel-l2-tests/.cargo/config.toml) ;
production inchangée.
F11 — La mailbox d'événements N1 est LIFO — ⚠️ épinglé, à arbitrer
notify_scheduler/poll_scheduler_events s'appuient sur IntrusiveMailbox
(stack documentée « order is reversed on reception ») : les événements
arrivent en ordre INVERSE d'émission. Sans gravité pour des événements
indépendants, mais toute logique future dépendant de l'ordre causal
(SchedTick avant Wakeup…) héritera de cette inversion. Épinglé AS IMPLEMENTED.
F12 — get_initfs_file_bytes ne retrouve pas les fichiers enregistrés avec préfixe — ⚠️ à vérifier au runtime
Le boot enregistre les modules Limine avec leur chemin complet
(/initfs/fs-ext4, cf. log « Registered /initfs/fs-ext4 »), mais le lookup
strip /initfs/ avant recherche : clé stockée /initfs/fs-ext4, clé cherchée
fs-ext4 → raté. Conséquence possible : exec d'initfs via ce chemin cassé.
Épinglé dans kernel_vfs_scheme.rs ; à confirmer sur le runtime QEMU.
F13 — Écrire dans un pipe à lecture fermée boucle à l'infini — ✅ CORRIGÉ
Trouvé par le harnais L2 (le test pendait indéfiniment) :
Pipe::write évalue la condition « read end closed → EPIPE » via
wait_until, mais le bras Err(_) => {} du match avalait l'erreur et
relançait la boucle — spin 100% CPU infini au lieu de retourner EPIPE.
Violation directe du contrat POSIX (SIGPIPE/EPIPE). Fix : propagation de
l'erreur fatale (Err(e) => return Err(e)), seul Again reste transitoire.
Impact production réel : tout processus écrivant dans un pipe dont le
lecteur est mort (shell pipelines, IPC interne) se figeait.
F14 — Limine v8.7 panique sur les kernels fraîchement construits — 🔍 investigation ouverte
Constaté en tentant la validation L3/L4 : Limine PANIC: elf: No higher half PHDRs exist au chargement de boot():/boot/kernel.elf, sur NOTRE
kernel selftest ET sur le kernel release reconstruit le 25/08 15:02.
Pourtant readelf -l montre 4 PT_LOAD en higher-half (0xffffffff8000_0000+),
entry point compris, ET_EXEC non-relocatable — le check elf64_get_ranges
de Limine v8.7.0 (common/lib/elf.c:646) devrait compter 4 ranges.
Éléments discriminants :
- les binaires Limine sont identiques (md5) dans les deux worktrees (clone v8.x-binary @ aad3edd, jan 2025) ;
- l'ISO construite ce matin depuis le VIEUX kernel (mai) bootait ;
- le kernel a donc changé, pas Limine → propriété ELF subtile en cause
(memsz ~2 Go de .bss par segment ? ordre/alignement des PHDRs ?).
⚠️ CONTEXTE : le flux de boot est en migration (bootloader perso BIOS
workspace/bootloader+create-image.sh, commits UEFI sur d'autres branches). À arbitrer : réparer la chaîne Limine (utile pour les selftests L3/L4 automatisés) ou basculer le harnais QEMU sur le bootloader perso. En attendant, la porte CI L0/L1/L2 (364 tests) ne dépend pas du boot QEMU.
Couverture ajoutée (L2 — code kernel réel sur hôte)
| Suite | Tests | Périmètre |
|---|---|---|
| inline kernel récupérés | 13 | lockfree_ring (8), mailbox (3), boot/toml (3*) |
kernel-l2-tests/tests/buddy_allocator.rs | 6 | buddy réel sur mémoire hôte: alignement par ordre, épuisement→OOM propre, zéro après purpose-alloc, stress 2000 ops avec payload tags, tolérance double-free |
kernel-l2-tests/tests/kernel_sync_namespace.rs | 10 | FixedQueue FIFO/wraparound/overflow, SpinLock réel, namespace bind/unbind/resolve longest-prefix |
kernel-l2-tests/tests/kernel_channels.rs | 9 | canal MPMC typé (multi-producteurs, disconnect lifecycle), SyncChan registre + drain-first après destroy |
kernel-l2-tests/tests/kernel_n1_semaphores.rs | 6+2 inline | événements N1 (ordre LIFO épinglé), sémaphores POSIX (comptage, destroy, registre) |
| kernel-l2-tests/tests/kernel_vfs_scheme.rs | 7 | finalize_pseudo_stat, registre KernelScheme (/initfs), routeur global de schemes, F12 |
| kernel-l2-tests/tests/kernel_vfs_fd.rs | 13 | OpenFile (offset partagé POSIX, permissions, EOF), FileDescriptorTable (réuse plus bas fd, dup F_DUPFD, cloexec), Nice saturé + AtomicNice |
| kernel-l2-tests/tests/kernel_pipes.rs | 9 | pipes kernel: FIFO, wraparound 4096, EOF/EPIPE lifecycle (F13), registre PipeScheme |
Total porte CI : 364 tests verts.
Reste candidat L2 : classes d'ordonnancement complètes (nécessitent une fake AddressSpace), fake userslice pour handlers read/write d'IpcScheme.
Total porte CI : 327 tests verts.
Reste candidat L2 (documenté) : vfs/scheme.rs + routeur — bloqué par
memory/userslice.rs qui valide les pointeurs userspace contre les tables
de pages x86 réelles ; nécessitera une fake de validation de région.
* comptage approximatif des modules inclus.
Harnais : crate ombre workspace/kernel-l2-tests — modules kernel inclus
verbatim via #[path], fakes fonctionnels pour IRQ/percpu/silo/HHDM.
| Suite | Tests | Périmètre |
|---|---|---|
abi/tests/abi_stability.rs | 10 | golden syscalls (169), layouts, magics |
abi/tests/errno_abi.rs | 4 | valeurs Linux, convention de détection |
abi/tests/flags_translation.rs | 8 | POSIX↔Strat9 exhaustif (1536 combos) |
abi/tests/ip_parsing.rs | 9 | IPv4/IPv6 edge cases |
abi/tests/ipc_codec_roundtrip.rs | 16 | codec bornes/roundtrip/endian |
abi/tests/ipc_payload_wire.rs | 12 | wire format schemes VFS |
fs-abstraction/tests/safe_math_edge_cases.rs | 23 | arith safe exhaustive |
fs-abstraction/tests/types_conformance.rs | 13+1 | mode bits, FILETIME, caps |
abi/tests/ipc_handshake.rs | 8 | handshake IPC wire + validation réservés |
abi/tests/data_types.rs | 9 | SiloMode, DirentHeader, PCI, tailles structs |
syscall/tests/error_and_flags.rs | 8 | Error↔errno roundtrip, demux RAX, flags Linux |
syscall/tests/dirent_wire.rs | 10 | getdents packed parsing, SchemeV2, SigAbi |
intel-ethernet/tests/descriptors.rs | 10 | layouts SDM 16B, anneaux Rx/Tx |
driver-net-proto/tests/wire_protocol.rs | 3 | opcodes + headers IPC réseau |
alloc-freelist/tests/allocator.rs | 6 | GlobalAlloc: alignement, réuse, OOM propre |
Porte CI : cargo make ci-tests — 162+ tests hôte verts (stage GitLab test).
Porte CI : cargo make ci-tests (stage GitLab test, job test-host).
Prochaines étapes proposées (L2/L3/L4)
- L2 kernel-hôte : extraire la logique pure IPC/mémoire en modules testables hôte.
- L3 : généraliser le pattern selftest (
PASS/FAILsérie grep-able) à tous les sous-systèmes kernel. - L4 : harnais QEMU automatisé (boot ISO + grep série + timeout) en job GitLab.