m³OS docs GitHub

Phase 50

IPC Completion

m3OS gains the missing IPC and bulk-data pieces required for serious userspace services: safe capability transfer, practical large-payload transport, ring-3-safe service registration, and clear failure semantics for the canonical server loop.

Complete

Learning goal

Understand why control flow and bulk-data transport must be designed together in a microkernel-style system.

Category

infrastructure

Why this phase matters

m3OS gains the missing IPC and bulk-data pieces required for serious userspace services: safe capability transfer, practical large-payload transport, ring-3-safe service registration, and clear failure semantics for the canonical server loop.

How it works in m3OS

Phase 50 finishes the IPC transport model that Phase 6 introduced as a control-path-only design. The kernel now supports safe capability transfer between process capability tables, a two-tier bulk-data mechanism (validated copy for small payloads, page grants for large transfers), ring-3-safe service registration with owner tracking and re-registration, and documented server-loop failure semantics. These additions close the gap between the existing message-passing infrastructure and the requirements of real ring-3 services.

How it builds on earlier phases

This phase builds on Architectural Declaration and turns that foundation into IPC Completion.

Implementation outline

  • Audit current IPC shortcuts and identify every service path that still depends on shared kernel addresses.
  • Finish and document the capability-grant semantics.
  • Implement the generic bulk-data transport model and its ownership rules.
  • Remove kernel-only assumptions from the service registry and service-address paths.
  • Standardize the canonical server loop and service-death semantics.
  • Port at least one real service path to the new transport as proof.
  • Extend validation so later extraction phases can depend on this transport with confidence.

Task progress snapshot

134 of 134 tracked tasks are complete.

Documentation targets

  • Documentation deliverables have not been listed yet.

Deferred follow-on work

  • Deep performance tuning of zero-copy paths
  • Rich typed service IDLs or code-generated message bindings
  • Advanced delegation patterns beyond the basic capability and buffer model

Source materials

Important code

Code spotlight cards

Code spotlight

Enumerate kernel-pointer shortcuts in IPC service syscalls

kernel/src/ipc/mod.rs L270-L293

Enumerate kernel-pointer shortcuts in IPC service syscalls. Why it matters: The safety comments at ipc_register_service (line 229) and ipc_lookup_service (line 254) explicitly state that name pointers are dereferenced as raw kernel addresses because all callers are kernel tasks — this assumption must be catalogued before it can be removed. Focus on `ipc_register_service` and `ipc_lookup_service` in `kernel/src/ipc/mod.rs` while you trace the snippet.

/// Syscall 9: register a named endpoint in the service registry.
///
/// `name_ptr` is a userspace virtual address pointing to `name_len` bytes of
/// UTF-8. The name is safely copied from the caller's address space via
/// `copy_from_user`. Invalid or unmapped pointers return an error.
fn ipc_register_service(ep_id: EndpointId, name_ptr: u64, name_len: u64) -> u64 {
    if name_ptr == 0 {
        return u64::MAX;
    }
    if name_len > 32 {
        return u64::MAX;
    }
    let name_len = name_len as usize;
    let mut name_buf = [0u8; 32];
    if crate::mm::user_mem::copy_from_user(&mut name_buf[..name_len], name_ptr).is_err() {
        return u64::MAX;
    }
    let name = match core::str::from_utf8(&name_buf[..name_len]) {
        Ok(s) => s,
        Err(_) => return u64::MAX,
    };
    match registry::register(name, ep_id) {
        Ok(()) => 0,
        Err(_) => u64::MAX,

Code spotlight

Write failing tests for capability grant through IPC messages

kernel-core/src/ipc/capability.rs L95-L117

Write failing tests for capability grant through IPC messages. Why it matters: TDD requires failing tests before implementation — the grant path must verify that a capability can be atomically moved from one table to another without duplication or loss. Focus on `CapabilityTable::grant` and `kernel-core` in `kernel-core/src/ipc/capability.rs` while you trace the snippet.

    /// Atomically transfer a capability from `self[source_handle]` to `dest_table`.
    ///
    /// On success the source slot is cleared and the new handle in the
    /// destination table is returned.  On failure (invalid source handle or
    /// destination table full) the source retains its capability and an error
    /// is returned — no side effects.
    pub fn grant(
        &mut self,
        source_handle: CapHandle,
        dest_table: &mut CapabilityTable,
    ) -> Result<CapHandle, CapError> {
        // Validate source handle first, without removing yet.
        let cap = self.get(source_handle)?;

        // Try to insert into destination — if it fails, source keeps the cap.
        let dest_handle = dest_table.insert(cap)?;

        // Destination succeeded — now remove from source (infallible since we
        // already validated the handle above and hold &mut self).
        let _ = self.remove(source_handle);

        Ok(dest_handle)
    }

Code spotlight

Extend Message to carry a capability slot

kernel-core/src/ipc/message.rs L3-L12

Extend Message to carry a capability slot. Why it matters: The current Message struct has no field for capability transfer — services cannot exchange authorities through the existing message format. Focus on `Message` and `new` in `kernel-core/src/ipc/message.rs` while you trace the snippet.

/// A small, register-sized IPC message.
#[derive(Debug, Clone, Copy, Default)]
pub struct Message {
    /// Operation identifier, chosen by convention between sender and receiver.
    pub label: u64,
    /// Inline data payload — up to 4 machine words.
    pub data: [u64; 4],
    /// Optional capability transferred with this message.
    pub cap: Option<Capability>,
}

Key files

  • kernel/Cargo.toml — When the phase lands, bump kernel/Cargo.toml and any release/version references to 0.50.0.
  • kernel/src/ipc/mod.rs — copy_from_user in register/lookup, sys_cap_grant
  • kernel/src/main.rs — | console_server_task write payload | kernel/src/main.rs | 350–361 | Still uses copy_nonoverlapping — kernel-task shortcut; migration planned for ring-3 move |
  • kernel/src/mm/user_mem.rs — copy_from_user / copy_to_user implementations
  • kernel-core/src/ipc/capability.rs — CapabilityTable::grant, Capability::Grant variant
  • kernel-core/src/ipc/message.rs — Message.cap field, updated constructors
  • kernel-core/src/ipc/buffer.rs — validate_user_buffer pure-logic address checks
  • kernel-core/src/ipc/registry.rs — Owner-tracked registry, replace_service, capacity 16
  • kernel/src/ipc/endpoint.rs — Capability transfer during message delivery
  • kernel/src/arch/x86_64/syscall/ipc.rs — IPC syscall dispatch module

How real OSes differ

  • Mature microkernels often invest heavily in message layout, grant semantics, and zero-copy transport much earlier.
  • Monolithic kernels avoid some of this design pressure by keeping drivers and services in one address space, at the cost of a larger TCB.
  • m3OS should favor a clear, teachable, reusable transport contract over premature optimization.

Success criteria

  • Capabilities can be granted safely and atomically between isolated processes.
  • There is a documented and implemented bulk-data path suitable for storage, packet, and framebuffer-sized transfers.
  • Core service registration no longer relies on kernel-task-only assumptions.
  • At least one representative service path uses the new transport without kernel-pointer shortcuts.
  • The IPC docs and validation coverage describe the same control flow and failure semantics the code actually implements.

Related phases

Keep exploring the roadmap