Phase 54
Deep Serverization
m3OS moves beyond proof-of-concept extraction and pushes real storage, namespace, and networking responsibilities out of the kernel. The kernel becomes a thinner object and transport layer, while userspace services own more of the policy-heavy behavior that currently dominates the trusted computing base.
Learning goal
Understand why storage, namespace, and networking are the hardest parts of microkernel convergence.
Category
infrastructure
Components touched
Builds on
Unlocked by this phase
Why this phase matters
m3OS moves beyond proof-of-concept extraction and pushes real storage, namespace, and networking responsibilities out of the kernel. The kernel becomes a thinner object and transport layer, while userspace services own more of the policy-heavy behavior that currently dominates the trusted computing base.
How it works in m3OS
Phase 54 moves meaningful storage, pathname, and networking policy out of ring 0 without breaking the existing Linux-like syscall ABI. The kernel keeps the object handles, transport, and bootstrap fallback paths; supervised userspace services now own the migrated policy slices: The result is not a fully serverized OS, but it is a materially narrower kernel than the Phase 53 baseline and a more honest microkernel story.
How it builds on earlier phases
This phase builds on Kernel Memory Modernization and turns that foundation into Deep Serverization.
Implementation outline
- Choose the first storage and network paths to move behind userspace services.
- Define the thin kernel object/handle facade required to preserve the current ABI.
- Move filesystem and namespace policy outward while keeping the syscall contract stable.
- Move at least one meaningful network or socket-facing policy path outward.
- Validate restartability, error handling, and performance well enough to document the trade-offs honestly.
- Update architecture, storage, and network docs to reflect the shipped boundaries.
Task progress snapshot
No checklist tasks were discovered for this phase yet.
Documentation targets
- Documentation deliverables have not been listed yet.
Deferred follow-on work
- Broader filesystem matrix beyond the first migrated path
- Full network-service ecosystem and higher-level userland daemons
- Aggressive performance tuning once the boundary is correct
- Complete POSIX policy removal from the kernel
Source materials
Important code
Code spotlight cards
Code spotlight
Task A1 — Route the first rootfs storage path through ring 3
path crosses a supervised ring-3 service boundary. Why it matters: Phase 54 is not real unless at least one meaningful storage. Focus on `sys_open` and `open_resolved_path` in `kernel/src/arch/x86_64/syscall/mod.rs` while you trace the snippet.
fn open_resolved_path(name: &str, flags: u64, mode_arg: u64) -> u64 {
// Decode POSIX access mode (O_ACCMODE = 0o3).
let (readable, writable) = match flags & 0o3 {
0 => (true, false), // O_RDONLY
1 => (false, true), // O_WRONLY
2 => (true, true), // O_RDWR
_ => return NEG_EINVAL, // invalid combination
};
// Phase 27: Permission check for existing files.
let create = (flags & 0x40) != 0; // O_CREAT
let file_meta = path_metadata(name);
if (!create || file_meta.is_some())
&& let Some((fu, fg, fm)) = file_meta
{
let (_, _, euid, egid) = current_process_ids();
let required = (if readable { 4u8 } else { 0 }) | (if writable { 2u8 } else { 0 });
if required != 0 && !check_permission(fu, fg, fm, euid, egid, required) {
return NEG_EACCES;
}
}
// Phase 27: When creating a new file, check parent directory write+execute permission.
if create Code spotlight
Task A2 — Stand up the storage services
service, not just a design note. Why it matters: The storage boundary must be backed by a real supervised. Focus on `program_main` and `populate_ext2_files` in `userspace/fat_server/src/main.rs` while you trace the snippet.
fn program_main(_args: &[&str]) -> i32 {
syscall_lib::write_str(STDOUT_FILENO, "fat_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, "fat_server: create_endpoint failed\n");
return 1;
}
let ep_handle = ep_handle as u32;
// 2. Register as "fat" service.
let ret = syscall_lib::ipc_register_service(ep_handle, "fat");
if ret == u64::MAX {
syscall_lib::write_str(STDOUT_FILENO, "fat_server: register_service failed\n");
return 1;
}
syscall_lib::write_str(
STDOUT_FILENO,
"fat_server: registered, entering recv loop\n",
);
// 3. Supervised recv loop — reply with "not implemented" to any request. Code spotlight
Task A2 — Stand up the storage services
service, not just a design note. Why it matters: The storage boundary must be backed by a real supervised. Focus on `program_main` and `populate_ext2_files` in `xtask/src/main.rs` while you trace the snippet.
/// Populate the ext2 partition image with initial directories and files
/// using `debugfs -w`. Creates temp host files for the `write` command.
fn populate_ext2_files(
part_path: &Path,
output_dir: &Path,
enable_telnet: bool,
smoke_test_mode: bool,
) {
// Standard Unix root filesystem layout.
let passwd_content =
"root:x:0:0:root:/root:/bin/ion\nuser:x:1000:1000:user:/home/user:/bin/ion\n";
// Pre-provisioned password hashes for CI/testing. Format: $sha256$hex_salt$hex_hash
// where hash = SHA-256(salt_bytes || password_bytes). Passwords: root="root", user="user".
let shadow_content = "root:$sha256$63695f726f6f745f73616c7431323334$5c8e5a851fee488aae9fc5890dd433f8a391fba2860899c271a6e6f5d3e4c439::::::\nuser:$sha256$63695f757365725f73616c7435363738$64fb26f3575e26ed5fc3b07e6c4ca2b6af8bf1f17267c34babb76448301a16ca::::::\n";
let group_content = "root:x:0:root\nuser:x:1000:user\n";
// Phase 46: service definition files.
let sshd_conf = "name=sshd\ncommand=/bin/sshd\ntype=daemon\nrestart=always\nmax_restart=10\ndepends=syslogd\n";
let telnetd_conf = "name=telnetd\ncommand=/bin/telnetd\ntype=daemon\nrestart=always\nmax_restart=10\ndepends=syslogd\n";
let syslogd_conf = "name=syslogd\ncommand=/bin/syslogd\ntype=daemon\nrestart=always\nmax_restart=10\ndepends=\n";
let crond_conf = "name=crond\ncommand=/bin/crond\ntype=daemon\nrestart=always\nmax_restart=10\ndepends=syslogd\n";
// Phase 52: service definitions for extracted ring-3 services.
let console_conf = "name=console\ncommand=/bin/console_server\ntype=daemon\nrestart=always\nmax_restart=10\ndepends=\n"; Key files
- kernel/Cargo.toml — When the phase lands, bump kernel/Cargo.toml and any release/version references to 0.54.0.
- userspace/fat_server/src/main.rs — Supervised storage-service endpoint
- userspace/vfs_server/src/main.rs — Migrated rootfs pathname and metadata policy
- userspace/net_server/src/main.rs — Migrated UDP policy service
- userspace/coreutils-rs/src/service.rs — Service-stop completion wait used by validation and operators
- userspace/init/src/main.rs — Degraded-mode logging, restart policy, and service-stop behavior
- userspace/login/src/main.rs — Hardened passwd/shadow reads against early-boot timing during validation
- kernel/src/arch/x86_64/syscall/mod.rs — Thin syscall facade, fallback paths, and service routing
- kernel/src/process/mod.rs — Signal delivery now wakes blocked IPC waiters for dying services
- kernel/src/ipc/endpoint.rs — IPC wait cancellation for signaled tasks
- kernel/src/task/scheduler.rs — Blocked IPC task enumeration for signal wakeup
- xtask/src/main.rs — Regression harness, smoke flow, and ext2 data-disk provisioning
How real OSes differ
- Mature microkernels often accept more coordination overhead to gain stricter isolation earlier.
- Linux keeps storage and networking in the kernel for very different historical and performance reasons.
- m3OS should focus on making the ownership split explicit, testable, and understandable before chasing every optimization.
Success criteria
- At least one real storage path runs through userspace-owned block/filesystem service logic.
- The namespace/VFS split is thinner and explicitly documented as kernel mechanism versus userspace policy.
- At least one meaningful networking or socket-facing path runs through userspace-owned service logic.
- Existing applications continue to work through a stable syscall facade while the implementation moves outward.
- Updated docs can explain the new call/data flow concretely instead of only describing the intended architecture.
Related phases
Keep exploring the roadmap
Phase 53
CompleteKernel Memory Modernization
The kernel memory subsystem becomes SMP-scalable and auditable: per-CPU page caching removes the global frame-allocator lock from the common single-page path, a magazine-based slab allocator with out-of-line slab metadata replaces the linked-list heap for small kernel objects, and the allocator preserves Phase 52b's zero-before-user-exposure guarantee while eliminating the double-lock and O(n) buddy removal paths.
Phase 54
In progressPost Serverization Kernel Hygiene
Close the `FdEntry` CLOEXEC / NONBLOCK plumbing gap and relocate the four `arch::x86_64::syscall::*_pub` wrappers into their owning subsystems so later phases do not inherit a silent security bug or a process-module arch dependency.