m³OS docs GitHub

Phase 52

First Service Extractions

m3OS proves that its microkernel direction is real by extracting at least the first visible core services out of the kernel address space. Console and input-related functionality become supervised ring-3 services, and the system demonstrates that they can fail and be restarted without rebooting the machine.

Complete

Learning goal

Understand how a kernel-resident service becomes a real ring-3 process.

Category

infrastructure

Why this phase matters

m3OS proves that its microkernel direction is real by extracting at least the first visible core services out of the kernel address space. Console and input-related functionality become supervised ring-3 services, and the system demonstrates that they can fail and be restarted without rebooting the machine.

How it works in m3OS

Phase 52 proves that the m3OS microkernel architecture is real by extracting console rendering and keyboard input handling from kernel-resident tasks into supervised ring-3 services. After this phase, the system demonstrates that user-visible subsystems can cross the ring-0 boundary, communicate through the Phase 50 IPC contract, and be restarted by the Phase 51 service manager without rebooting the machine.

How it builds on earlier phases

This phase builds on Service Model Maturity and turns that foundation into First Service Extractions.

Implementation outline

  • Choose the first extraction targets and define their ring-3 service contracts.
  • Narrow the kernel side to the minimal notification, mapping, and hardware-mediation responsibilities.
  • Wire the new services into build, initrd, and service-manager configuration.
  • Port console/input policy out of kernel-resident task code.
  • Implement restart and reconnect behavior for the extracted services.
  • Measure the boundary cost and document the new call/data flow.
  • Add smoke or focused validation that proves restartability and correct behavior.

Task progress snapshot

23 of 27 tracked tasks are complete.

Documentation targets

  • Documentation deliverables have not been listed yet.

Deferred follow-on work

  • Storage, namespace, and networking extraction
  • Rich multi-seat or multi-session input policy
  • Fully graphical display ownership
  • Broad performance tuning of the new boundary

Source materials

Important code

Code spotlight cards

Code spotlight

Stand up a ring-3 console service endpoint

userspace/console_server/src/main.rs L33-L56

Stand up a ring-3 console service endpoint. Why it matters: Phase 52 is only meaningful if the console becomes a real userspace service with its own endpoint and registration path instead of remaining a kernel-only concept. Focus on `program_main` and `console_server` in `userspace/console_server/src/main.rs` while you trace the snippet.

fn program_main(_args: &[&str]) -> i32 {
    syscall_lib::write_str(STDOUT_FILENO, "console_server: starting\n");

    // Phase 52 transitional: skip framebuffer_mmap because it calls
    // try_yield_console which suppresses ALL kernel framebuffer text output
    // (login prompt, shell, everything).  The kernel's fb console still
    // handles sys_write rendering.  The userspace console_server runs as an
    // IPC-only service: CONSOLE_WRITE payloads are echoed to stdout which
    // the kernel renders to both serial and framebuffer.
    //
    // Full framebuffer takeover will be enabled in a later phase when
    // sys_write(STDOUT) is routed through IPC to this service.

    // 1. Create an IPC endpoint.
    let ep_handle = syscall_lib::create_endpoint();
    if ep_handle == u64::MAX {
        syscall_lib::write_str(STDOUT_FILENO, "console_server: create_endpoint failed\n");
        return 1;
    }
    let ep_handle = ep_handle as u32;

    // 2. Register as "console" service (takes over from kernel entry).
    let ret = syscall_lib::ipc_register_service(ep_handle, "console");
    if ret == u64::MAX {

Code spotlight

Move keyboard IRQ service ownership to ring 3

userspace/kbd_server/src/main.rs L16-L39

Move keyboard IRQ service ownership to ring 3. Why it matters: This is the first completed hardware-facing service extraction in Phase 52: IRQ-driven keyboard delivery no longer depends on a kernel-resident kbd_server_task. Focus on `program_main` and `kbd_server_task` in `userspace/kbd_server/src/main.rs` while you trace the snippet.

fn program_main(_args: &[&str]) -> i32 {
    syscall_lib::write_str(STDOUT_FILENO, "kbd_server: starting\n");

    // 1. Create an IPC endpoint.
    let ep_handle = syscall_lib::create_endpoint();
    if ep_handle == u64::MAX {
        syscall_lib::write_str(STDOUT_FILENO, "kbd_server: failed to create endpoint\n");
        return 1;
    }
    let ep_handle = ep_handle as u32;

    // 2. Register as "kbd" in the service registry.
    let ret = syscall_lib::ipc_register_service(ep_handle, "kbd");
    if ret == u64::MAX {
        syscall_lib::write_str(STDOUT_FILENO, "kbd_server: failed to register 'kbd'\n");
        return 1;
    }

    // 3. Create an IRQ1 notification.
    let notif_cap = syscall_lib::create_irq_notification(1);
    if notif_cap == u64::MAX {
        syscall_lib::write_str(
            STDOUT_FILENO,
            "kbd_server: failed to create IRQ1 notification\n",

Code spotlight

Bridge keyboard IPC back into the existing stdin path

userspace/stdin_feeder/src/main.rs L86-L109

Bridge keyboard IPC back into the existing stdin path. Why it matters: Existing userspace still reads stdin through the traditional TTY path, so Phase 52 needs a bridge from the new keyboard IPC service back into the shared kernel line discipline. Focus on `program_main` and `sys_push_raw_input` in `userspace/stdin_feeder/src/main.rs` while you trace the snippet.

fn program_main(_args: &[&str]) -> i32 {
    syscall_lib::write_str(STDOUT_FILENO, "stdin_feeder: starting\n");

    // Look up the "kbd" service to obtain an endpoint capability.
    // Retry with short sleeps because the service manager starts us as soon
    // as kbd_server is forked, which is before it has registered its IPC
    // endpoint.
    let kbd_handle = {
        let mut h = u64::MAX;
        for _ in 0..50 {
            h = syscall_lib::ipc_lookup_service("kbd");
            if h != u64::MAX {
                break;
            }
            syscall_lib::nanosleep(20_000_000); // 20 ms
        }
        if h == u64::MAX {
            syscall_lib::write_str(STDOUT_FILENO, "stdin_feeder: failed to lookup 'kbd'\n");
            return 1;
        }
        h as u32
    };

    syscall_lib::write_str(STDOUT_FILENO, "stdin_feeder: ready\n");

Key files

  • kernel/Cargo.toml — When the phase lands, bump kernel/Cargo.toml and any release/version references to 0.52.0.
  • userspace/console_server/src/main.rs — Ring-3 console rendering service
  • userspace/kbd_server/src/main.rs — Ring-3 keyboard scancode service
  • userspace/stdin_feeder/src/main.rs — Ring-3 scancode-to-byte bridge that forwards via push_raw_input
  • userspace/init/src/main.rs — Service manager integration for extracted services
  • kernel-core/src/tty.rs — Shared kernel line discipline for serial and keyboard input
  • kernel/src/arch/x86_64/syscall/mod.rs — push_raw_input entry point and TTY-facing syscall glue
  • kernel/src/main.rs — Narrowed kernel-side mediation (FB mapping, IRQ, buffers)
  • kernel/src/ipc/mod.rs — Registry and capability infrastructure
  • kernel/initrd/etc/services.d/console.conf — Console service definition
  • kernel/initrd/etc/services.d/kbd.conf — Keyboard service definition

How real OSes differ

  • Mature microkernels often perform this extraction work earlier and across more subsystems at once.
  • Monolithic kernels keep console and input behavior in-kernel for historical and performance reasons, not because that is the only workable design.
  • m3OS should optimize here for clarity, restartability, and teaching value over perfect early performance.

Success criteria

  • At least one previously kernel-resident core service runs as a real supervised ring-3 process.
  • The extracted service uses the Phase 50 IPC path without shared-address-space shortcuts.
  • Restarting or crashing the extracted service does not require a full machine reboot to recover basic functionality.
  • The service graph, build system, and docs all describe the extracted service as part of the normal system model.
  • Boundary measurements and trade-offs are written down for later phases.

Related phases

Keep exploring the roadmap