Sprint 7 β€” Init Process & First Real Program

Life outside the kernel.

πŸ”² Planned

Table of contents

Overview #

Sprint 7 is where MinimalOS becomes a real operating system. The kernel loads the init process β€” the first userspace program β€” and everything after that happens through capability-mediated IPC. This sprint also creates the userspace library (libmnos) that provides safe Rust wrappers around the raw syscall interface.


Init Process #

πŸ”² Not Yet Implemented

Role

The init process is the root of the process tree and the service manager. It's the only process that receives capabilities directly from the kernel at boot β€” all other processes receive their capabilities from init or its children.

Boot Capabilities

The kernel creates these capabilities and places them in init's capability table:

SlotTypeResourceRights
0MemoryAll usable RAMRead, Write, Grant
1IPC EndpointKernel logSend
2ProcessSelfAll
3InterruptAll unmasked IRQsReceive, Grant

Responsibilities

  1. Spawn child processes β€” load service binaries from the initramfs
  2. Distribute capabilities β€” give each service the minimum capabilities it needs
  3. Service registration — maintain a name→endpoint directory ("serial_driver" → endpoint #7)
  4. Service lookup β€” respond to queries: "where is the VFS service?"
  5. Crash recovery β€” restart failed services

Service Registration Protocol

    graph LR
      subgraph SD["Init Process Service Directory"]
        S1["serial_driver"] --> E1["endpoint_7"]
        S2["vfs_service"] --> E2["endpoint_12"]
        S3["network_stack"] --> E3["endpoint_15"]
      end
    

Services register with init via IPC:

β†’ IPC_SEND(init_endpoint, { type: REGISTER, name: "serial_driver", endpoint: my_endpoint })
← IPC_REPLY({ status: OK })

Clients look up services:

β†’ IPC_SEND(init_endpoint, { type: LOOKUP, name: "serial_driver" })
← IPC_REPLY({ status: OK, endpoint: serial_endpoint_cap })

Userspace Library (libmnos) #

πŸ”² Not Yet Implemented

Purpose

libmnos is a static library that userspace programs link against. It provides safe Rust abstractions over the raw SYSCALL interface.

Syscall Wrappers

// Raw syscall (unsafe)
unsafe fn syscall3(nr: u64, arg1: u64, arg2: u64, arg3: u64) -> i64 {
    let ret: i64;
    asm!(
        "syscall",
        in("rax") nr,
        in("rdi") arg1,
        in("rsi") arg2,
        in("rdx") arg3,
        out("rcx") _,  // clobbered by SYSCALL
        out("r11") _,  // clobbered by SYSCALL
        lateout("rax") ret,
    );
    ret
}

// Safe wrapper
pub fn ipc_send(endpoint: CapSlot, msg: &IpcMessage) -> Result<(), IpcError> {
    let ret = unsafe { syscall3(SYS_IPC_SEND, endpoint.0 as u64, msg as *const _ as u64, 0) };
    match ret {
        0 => Ok(()),
        e => Err(IpcError::from_code(e)),
    }
}

IPC Helpers

// High-level RPC call
pub fn rpc_call<Req: Serialize, Resp: Deserialize>(
    endpoint: CapSlot,
    request: &Req,
) -> Result<Resp, IpcError> {
    let msg = IpcMessage::encode(request);
    let reply = ipc_call(endpoint, &msg)?;
    Ok(reply.decode()?)
}

Memory Allocation

Userspace gets its own heap allocator that requests pages from the kernel:

pub fn alloc_pages(count: usize) -> Result<*mut u8, MemError> {
    let addr = unsafe { syscall2(SYS_MEM_ALLOC, count as u64, 0) };
    if addr < 0 { Err(MemError::OutOfMemory) }
    else { Ok(addr as *mut u8) }
}

First Userspace Driver #

πŸ”² Not Yet Implemented

Serial Console Driver

The first real userspace service β€” a serial console driver that demonstrates the full capability-based architecture:

    sequenceDiagram
      participant K as Kernel
      participant I as Init Process
      participant S as Serial Driver
      K->>I: Boot capabilities
      I->>S: Spawn + caps (IRQ 4, MMIO, IPC endpoint)
      Note over K,S: Runtime operation
      K-->>S: IRQ 4 fires β†’ notify
      S->>S: Read serial port, process input
      S->>I: IPC: echo to output
    

What This Demonstrates

  1. Interrupt routing: IRQ 4 (COM1) is routed to the serial driver via an interrupt capability
  2. MMIO access: The driver has a memory capability for the serial port's I/O range
  3. Process isolation: The driver runs in Ring 3 β€” a bug in the driver can't crash the kernel
  4. IPC communication: The serial driver communicates with other processes via IPC
  5. Capability delegation: Init gives the serial driver exactly the capabilities it needs β€” nothing more

Initramfs #

The init process and initial services are bundled into an in-memory filesystem (initramfs) that Limine loads alongside the kernel:


Dependencies #