# MPOS **Repository Path**: zhou-yucong/mpos ## Basic Information - **Project Name**: MPOS - **Description**: Rust操作系统 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-10-09 - **Last Updated**: 2026-10-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # MPOS
![Rust 2024](https://img.shields.io/badge/Rust-2024-E8453C?style=for-the-badge&logo=rust&logoColor=white) ![Target x86_64](https://img.shields.io/badge/Target-x86__64-000000?style=for-the-badge&logo=amd&logoColor=white) ![Bootloader mpos-boot](https://img.shields.io/badge/Bootloader-mpos--boot-4A90D9?style=for-the-badge&logo=gnu&logoColor=white) ![License MIT](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge) **A Milestone-Verified Operating System Kernel** A uniprocessor x86_64 operating system kernel in Rust, booted on UEFI by its own loader. Every subsystem is proven correct before the next one is built — from the frame allocator to an interactive shell. Linux ABI compatible: musl-based binaries run unmodified. [Quick Start](#quick-start) · [Architecture](#architecture) · [Syscalls](#syscalls) · [Milestones](#milestone-checks) · [Building](#building) · [Testing](#testing) · [Docs](#documentation) · [中文](docs/README.zh.md)
--- ## Overview **MPOS** is a teaching and research operating system kernel built around a **milestone verification** methodology: rather than adding layers and hoping, each one is proven correct by assertions and tests before the next is built — and the proofs live in two places, so neither can rot quietly. - **On the host.** Architecture-independent logic lives in the kernel *library* (`src/lib.rs`): the frame allocator, the mapping plan, the scheduler's policy, path resolution, every filesystem's encoding, the pure half of each device's ioctl contract. It compiles for `x86_64-unknown-linux-gnu` and runs under plain `cargo test`. - **On the machine.** ~120 user-space programs are booted in QEMU and must report `=== PASS`; they speak the Linux ABI through real `syscall` instructions, with the numbers taken from the kernel's own `src/syscall/linux/nr.rs`, so kernel and tests cannot disagree about a number without one of them failing to build. Machine-dependent code — descriptor tables, port I/O, virtio rings, the boot handoff — is gated behind `#[cfg(target_os = "none")]`, which is what keeps the host build free of hardware. ### Core features | Feature | Description | |---------|-------------| | **Own UEFI loader** | `mpos-boot` reads the kernel, the initramfs and the command line off the ESP, builds the page tables and the higher-half mapping, applies the kernel's relocations and jumps — then stops existing. No third-party bootloader, no ISO | | **Linux ABI compatible** | 252 Linux x86_64 syscall numbers are named and routed; musl binaries (Alpine, busybox, `apk`, `fish`, weston) run unmodified | | **Framebuffer console** | virtio-gpu 2D scanout, a kernel console that *moves onto it* after boot, virtual consoles (`Ctrl-A 1..6`; the image runs a `getty` on `tty1`..`tty4`), and a best-effort serial mirror that never throttles the window | | **DRM/KMS + fbdev** | `/dev/fb0` with the fbdev ioctls and `/dev/dri/card0` speaking the Linux DRM UAPI — dumb buffers, page flip, vblank events, render node, PRIME. This is the path **weston** takes: its DRM backend through `seatd`, with Mesa's software GL (llvmpipe) over GBM dumb buffers. No GPU-side 3D and no virgl | | **Input** | virtio-keyboard and virtio-tablet as evdev nodes (`/dev/input/eventN`, capability ioctls, per-open-file grab), plus `/dev/uinput` so a program can create a device of its own | | **Write-through ext2/ext4** | Full read/write ext2, ext3 and ext4 — extents, directory hashing, 4-way set-associative block cache — with write-through semantics: no journal, no dirty tracking, crash-visible | | **Priority scheduler** | Strict priority across four bands (LOW, NORMAL, HIGH, CRITICAL), round-robin within a band, deadline-based sleeps, COW fork, demand-paged stack growth | | **Signals** | Handlers delivered through a real `rt_sigframe`, mask enforcement, `sigqueue`/`siginfo` payloads, `SIGKILL`/`SIGSTOP` unblockable | | **TCP/IP networking** | smoltcp-driven stack with TCP, UDP, raw ICMP and ARP over virtio-net, MSI-X interrupts, and event-driven waits that end a blocked reader when a frame arrives rather than at the next tick | | **AF_UNIX** | Path-based `SOCK_STREAM` with `SCM_RIGHTS` descriptor passing, `SCM_CREDENTIALS` and `SO_PEERCRED` — enough for a real `dbus` broker and `udev`'s worker channel | | **Interactive shell** | Alpine's OpenRC boot into a `getty` on a pty-backed `/dev/tty1` (four consoles by default, edited in `rootfs/etc/inittab`), with pipes, redirection, background jobs and a scriptable init | --- ## Architecture `docs/ARCHITECTURE.md` has the full module graph, the measured dependency edges, the boot sequence diagram and the layering rules. This is the summary. ### System overview ```mermaid graph TB subgraph UserSpace["User Space (Ring 3)"] MUSL["musl libc"] SHELL["/bin/sh · fish · busybox"] WESTON["weston (DRM/KMS + GL)"] TESTS["~120 ABI conformance programs"] end subgraph KernelSpace["Kernel Space (Ring 0)"] subgraph Gate["Syscall gate"] SYSCALL["syscall / sysret
252 numbers routed"] end subgraph VFSLayer["vfs"] VFS_CORE["Inode / FileSystem / mount / path"] RAMFS["ramfs · tmpfs · tar"] EXT4["ext2 / ext3 / ext4 (write-through)"] PSEUDO["procfs · sysfs · devfs · devpts"] P9FS["9p (host share)"] IPCFS["pipe · epoll · timerfd · eventfd
signalfd · inotify · memfd · dmabuf · locks"] TTYLAYER["tty · pty · term · vcmux"] end subgraph Proc["sched"] SCHED["4-band priority + round-robin"] SIGNAL["signals · futex · ptrace"] EXEC["fork / clone / execve / ELF"] end subgraph Mem["mm"] PMM["Buddy allocator (order 0-14)"] SLAB["Slab heap (16-2048 B)"] PAGE["4-level paging · W^X · COW"] end subgraph Net["net"] SMOLTCP["smoltcp: TCP · UDP · ICMP · ARP"] UNIXX["AF_UNIX · AF_VSOCK · netlink"] end subgraph Drv["drivers"] VBLK["virtio-blk"] VNET["virtio-net + MSI-X"] VGPU["virtio-gpu · DRM · fbdev · render"] VIN["virtio-input · evdev · uinput"] MISC["virtio-rng · virtio-sound · 9p · console · vsock"] PCIBUS["PCI enumeration · virtio HAL"] end CONSOLE["console: log ring · serial · framebuffer"] ARCHL["arch/x86_64: gdt · idt · pic · lapic · paging · timer"] end MUSL --> SYSCALL SHELL --> MUSL WESTON --> MUSL TESTS --> MUSL SYSCALL --> VFS_CORE SYSCALL --> SCHED SYSCALL --> SMOLTCP VFS_CORE --> RAMFS VFS_CORE --> EXT4 VFS_CORE --> PSEUDO VFS_CORE --> IPCFS EXT4 --> VBLK VNET --> SMOLTCP SMOLTCP --> UNIXX VIN --> TTYLAYER CONSOLE --> TTYLAYER VGPU --> CONSOLE SCHED --> PAGE EXEC --> SLAB PAGE --> PMM ``` ### Boot flow ```mermaid graph TD A["UEFI firmware"] --> B["EFI/BOOT/BOOTX64.EFI — mpos-boot"] B --> B1["read mpos.elf, initramfs.tar, mpos.cmdline from the ESP"] B1 --> B2["memory map · page tables · direct map at KERNEL_VIRT_BASE"] B2 --> B3["relocate the PIE image · exit boot services"] B3 --> C["_start(handoff) — ring 0, kernel stack, interrupts off"] C --> C1["console::init() · enable_user_sse() · handoff::collect()"] C1 --> D["mm::init() → logging::init()"] D --> E["init_arch: gdt · idt · pic · timer · rtc · syscall gate · paging::install()"] E --> F["init_sched: adopt the boot task"] F --> G["init_vfs: ramfs root, untar the initramfs"] G --> H["init_pci → init_blk: virtio-blk, GPT scan, mount /mnt"] H --> I["init_net: virtio-net, smoltcp, 10.0.2.15/24"] I --> J["init_virtio: rng · console · gpu · input · sound · 9p · vsock"] J --> K["rand::init(): ChaCha20 from RDRAND + host entropy"] K --> L["handoff_to_init: spawn /init as PID 1, park the boot task"] L --> M["initramfs /init: mount proc · sysfs · devtmpfs"] M --> N["mount /dev/vda2 (ext4), mount proc/sys/tmp/run, move /dev, devpts"] N --> O["switch_root /sysroot /sbin/init"] O --> P["OpenRC: services, devfs, networking"] P --> Q["shell on /dev/tty1 (pty-backed)"] ``` The kernel mounts nothing but its own ramfs root. `/proc`, `/sys`, `/dev`, the real root and the `switch_root` all belong to the initramfs's `/init` (`initramfs/init`), because a kernel that assembles them first leaves pieces that the owner of the system cannot unmount. ### Memory management ```mermaid graph LR subgraph Physical PMM["Buddy allocator
order 0-14, 4 KiB pages
max contiguous 64 MiB
intrusive free lists, O(1) coalesce"] end subgraph Heap SLAB["Slab allocator
16, 32, 64, 128, 256, 512, 1024, 2048 B"] LARGE["> 2048 B → direct frame allocation"] end subgraph Virtual PT["4-level page tables
PML4 → PDPT → PD → PT"] HHDM["Direct map
kernel half, huge pages"] USER["User space
demand-paged, COW, W^X, ASLR"] end PMM --> SLAB PMM --> LARGE PMM --> PT PT --> HHDM PT --> USER ``` ### Console, display and input ```mermaid graph LR VIN["virtio-input
keyboard + tablet"] --> EVDEV["evdev window
per-client position"] EVDEV --> INPUT["/dev/input/eventN"] EVDEV --> KEYS["keymap"] KEYS --> TERM["terminal emulator
6 virtual consoles"] TERM --> RENDER["render task
the only flush() caller"] RENDER --> GPU["virtio-gpu scanout"] GPU --> FB["/dev/fb0 (fbdev ioctls)"] GPU --> DRM["/dev/dri/card0 (DRM/KMS)
dumb buffers · page flip · vblank"] SERIAL["serial mirror
best-effort, never throttles"] --> TERM DRM --> WESTON["weston DRM backend
via seatd, software GL (llvmpipe)"] UINPUT["/dev/uinput"] --> EVDEV ``` ### Network data path ```mermaid graph LR APP["user program
socket() / read() / write()"] --> SOCKINODE["socket node
(vfs)"] SOCKINODE --> DISPATCH["net::socket handlers
buffer validation · errno shapes"] DISPATCH --> REG["socket registry
stable handles + subscriptions"] REG --> SMOLTCP["smoltcp interface
TCP · UDP · raw ICMP · ARP"] SMOLTCP --> DEVSEAM["Device seam (RxToken / TxToken)"] DEVSEAM --> VNETDRV["virtio-net driver"] VNETDRV --> MSIX["MSI-X vector"] VNETDRV --> SLIRP["QEMU SLIRP
10.0.2.2 gateway"] SLIRP --> HOST["host network"] MSIX --> WAKE["wake the subscribers"] WAKE --> REG ``` --- ## Syscalls `src/syscall/linux/nr.rs` names **252 Linux x86_64 syscall numbers**; every one of them is routed by the dispatch table in `src/syscall/linux/mod.rs`, and roughly 234 do real work — the handful that do not answer `ENOSYS`, which `PLAN.md` tracks as the remaining ABI gap (`fanotify`, `keyctl`, `userfaultfd`, real-time signals 33–64, `io_uring`). The POSIX interval timers (`timer_create` 222 … `timer_delete` 226) joined that set with the five numbers they are; `SIGRTMIN` (32) is queued, masked and waited for — the one real-time number musl's `SIGEV_THREAD` helper needs — while a *disposition* for it, and the range above it, is still refused. Where the handlers live, and why the files are as big as they are: | Handler module | Numbers | What it answers | |---|---:|---| | `syscall/linux/fs/file.rs` + `fs/ioctl.rs` | 135 | Everything that carries a path or drives a file descriptor: `open`/`openat`/`openat2`, `stat`/`statx`, `getdents64`, `renameat2`, `splice`, `mount`, `fcntl`, pipes, `sendfile`, `fallocate`, `statfs` — in `file.rs`; the device-`ioctl` entry point in `ioctl.rs`. The old single `fs.rs` split along that seam at PLAN.md §11.2 Step E | | `syscall/linux/proc.rs` | 50 | Memory and process machinery: `mmap`/`munmap`/`mremap`/`brk`/`mprotect`, `fork`/`clone`/`vfork`, `execve`, `exit_group`, `wait4`/`waitid`, `ptrace`, `process_vm_readv`/`writev`, `nanosleep`, `clock_gettime`, `gettimeofday` | | `syscall/linux/posix.rs` | 50 | The POSIX completion set: the identity family (`setreuid`, `getresuid`, `capget`…), capabilities, `sched_*`, `getpriority`/`setpriority`, memory advice (`msync`, `madvise`, `mlock`), the wall-clock setters, self-directed signals | | `syscall/linux/abi.rs` | 12 | Pure ABI facts — flag values, struct layouts, argument validation, encodings — host-compiled so the man-page contract is unit-testable | | `syscall/linux/nr.rs` | — | The numbers themselves, included by the test suite with `#[path]` so kernel and tests share one table | `ioctl` deserves a note: its request-number families for fbdev, DRM, RTC, uinput, evdev, TTY and block devices are parsed in `syscall/linux/fs/ioctl.rs` — the old single `fs.rs` grew past 6 300 lines holding that switchboard alongside the path/file calls, so PLAN.md §11.2 Step E split the file along that seam (the switchboard itself is unchanged). `PLAN.md` Phase 11 (Step B) is the plan to give each driver its own contract back. Deliberate divergences from Linux are collected per subsystem in `PLAN.md` — for example `listen(2)` on IPv4 is `EOPNOTSUPP` (the stack is outbound-only), `SO_LINGER{on,0}` is left orderly rather than faked, `unshare`/`setns` validate and then refuse, and `seccomp` speaks `STRICT` but not `FILTER`. They are recorded there rather than hidden here. --- ## Milestone checks Each subsystem is proven before the next is built, and the on-machine half of that proof prints markers to the serial log. They run **only** when the command line carries `mpos.selftest` — which `cargo xtask test` sets and an ordinary boot does not — so a normal boot pays none of the half-second clock wait and the scheduler sleeps these checks need. | Marker | Subsystem | What is verified | |:------:|-----------|------------------| | `M0_OK` | Allocator | The global allocator works end to end (`Vec`, `String`) | | `M1_OK` | Interrupts | The PIT ticks at the expected rate; a breakpoint exception is catchable and returns | | `M2_OK` | Frames & heap | Buddy blocks are disjoint and coalesce; every slab size class holds its contract; heap and frame allocator agree on what was handed out | | `M3_OK` | Paging | The kernel runs on its own page tables; `text` is `r-x`, `rodata` `r--`, `data` `rw-` across the whole image; user address spaces are isolated with a shared kernel half; both are torn down without leaking a frame | | `M4_OK` | Scheduler | Round-robin, preemption, sleep, idle wake-up, priority-based core stealing; every task reaped, every stack returned | | `M6_OK` | Syscalls | The dispatch gate answers correctly from inside a task; `exit_group` through dispatch ends it | | `M7_OK` | VFS | ramfs root, a mount crossed by resolution, two tasks reading and writing one file through the fd table | | `M8_IPC_OK` | Pipes, TTY, signals | Bytes cross a pipe between two tasks; `EPIPE` with no reader; line editing, echo and `Ctrl-D`; `SIGTERM`/`SIGKILL` land; the `kill(0)` probe answers | | `M8_OK` | Init handoff | `/init` runs as PID 1, forks, execs a shell, and stays alive | There is no M5, and nothing above M8 — the checks stop where the milestone numbering had a gap, and the numbering is kept as it is because the CI logs and `PLAN.md` refer to it. The `M0`–`M14` pipeline that earlier revisions of this README described never existed in this form; the authoritative record of what is proven is the test suite, not the markers. **The real gate is the self-test.** `cargo xtask test` runs the host unit tests and then boots the user test programs `tests.toml` selects — every program that is not in a group — requiring each to print `=== PASS` and the run to end with a `=== SUMMARY` line and QEMU exit code 33. The tests that need something outside the machine, or that measure rather than assert, are in groups and run by name (`--groups`, `--all-groups`), which is what keeps a red gate a statement about the kernel. See [Testing](#testing). --- ## Quick Start ### Prerequisites - **Rust nightly** with `rust-src`, `rustfmt`, `clippy` - Four targets: `x86_64-unknown-none` (kernel), `x86_64-unknown-uefi` (loader), `x86_64-unknown-linux-musl` (user programs), `x86_64-unknown-linux-gnu` (host tests + xtask) - **QEMU** (`qemu-system-x86_64`), **OVMF** (`OVMF_CODE.4m.fd` / `OVMF_VARS.4m.fd`) - `qemu-img`, `qemu-nbd`, `sfdisk`, `partx`, `blkid`, `mkfs.ext4`, `mkfs.fat`, `tar`, `sudo` - A network connection for the first `initramfs`/`rootfs` build (Alpine packages) ```bash rustup toolchain install nightly rustup component add rust-src rustfmt clippy --toolchain nightly rustup target add x86_64-unknown-none x86_64-unknown-uefi \ x86_64-unknown-linux-musl x86_64-unknown-linux-gnu ``` ### Build and run ```bash cargo xtask all # kernel + loader + initramfs + disk image + Alpine rootfs (needs sudo) cargo xtask run # boot it; rebuilds first if anything is stale ``` The kernel boots, brings up the initramfs, pivots into the Alpine root filesystem and drops you into an interactive shell — on the serial line, in the QEMU window, or in both, depending on the mode below. `cargo xtask run` takes a mode that decides how the window and the serial line are wired, and which consoles the guest is told it has. The guest is told with Linux's own `console=` words, and nothing else: every console on the list receives the log and the last one is the preferred console — the one whose writes wait rather than being dropped. | Mode | What you get | Guest `console=` | |------|--------------|------------------| | `terminal` | Serial only, no window — the lossless log; `/dev/fb0` still exists, nothing is drawn on it | `console=ttyS0` | | `gui` | The whole machine in the window: the text console — log, virtual consoles, login, shells — shows there, and the host terminal stays quiet with the serial line copied to `build/serial.log`; a compositor claimed it at runtime with `./start-weston.sh` | `console=tty0` | | `mirror` | The window plus a best-effort copy on the serial line (the default) | `console=ttyS0 console=tty0` | ```bash cargo xtask run --mode terminal # watch the boot log cargo xtask run --mode gui # the machine, in the window ``` Three modes, because the guest's consoles and the launcher's window are separate questions, and each mode owns an answer. What a command line can never say is *who is drawing the screen*: a compositor takes it at runtime, through `DRM_IOCTL_SET_MASTER` and `KDSETMODE`, whichever windowed mode it starts in. `xtask/README.md` documents every command, flag and environment variable, in English and Chinese. In short: | Command | What it does | Root? | |---|---|---| | `cargo xtask build` [`--arch x86_64\|aarch64`] | Kernel + UEFI loader, for the machine `--arch` names (it drives both `--target`s) | no | | `cargo xtask initramfs` | Build the initramfs archive | yes | | `cargo xtask disk` | Create the empty GPT image | yes | | `cargo xtask rootfs` | Install Alpine into the image (refreshes the initramfs) | yes | | `cargo xtask run` | Boot in QEMU, rebuilding when stale | when needed | | `cargo xtask all` | All of the above, in order | yes | | `cargo xtask test` | Host unit tests + the user test programs `tests.toml` selects, in QEMU | **no** | | `cargo xtask clean` | Remove `build/` and `target/` | when needed | --- ## Building The build is a Cargo workspace with one front-end. `xtask` compiles as your own user and escalates only for the steps that genuinely need it (writing a block device, mounting an image, `chroot`), handing the artefacts back afterwards. ```bash cargo xtask build # kernel and loader only cargo build --target x86_64-unknown-none # the same, without xtask ``` ### The disk image `cargo xtask all` assembles `build/mpos-uefi-x86_64.img`: a GPT disk with two partitions. | # | Name | Type | Size | Contents | |---|------|------|------|----------| | 1 | `efi` | FAT32, bootable | 128 MiB (from `rootfs.toml`) | `EFI/BOOT/BOOTX64.EFI` (the loader), `mpos.elf`, `initramfs.tar`, `mpos.cmdline` | | 2 | `system` | ext4 | rest of the 2 GiB image | The Alpine root filesystem — `/sbin/init`, OpenRC, musl, busybox, `apk` | QEMU boots it directly under OVMF: no ISO, no optical drive, no loop device, and no root needed for `cargo xtask test`, which boots a directory QEMU synthesizes as virtual FAT. ### What goes in the image `rootfs.toml` decides the packages, the filesystem, the sizes, the hostname, DNS and the user account; `machine.toml` decides what the image is booted on (memory, cpus, devices, the window, the standing run mode), which is a separate file so that tuning the machine cannot invalidate the image; `initramfs.toml` does the same for the early userspace. All three are one file each for *both* machines, in three sections: `[default]` is the baseline both read, and `[x86_64]`/`[aarch64]` are each machine's own half — so a value the second machine does not share is written once, under its own name, and the first machine never sees it. Concrete files live as real files under `rootfs/` and `initramfs/`, overlaid *after* anything written from the toml — so a file in the tree wins, which is how `/etc/inittab`, `/etc/network/interfaces` and `/init` are edited. ### Cross-architecture Not available, and the configuration no longer pretends otherwise: there is no `aarch64-unknown-none` profile in `.cargo/config.toml` and no such target in `rust-toolchain.toml`, because the profile that used to sit there named `arch/aarch64/linker.ld` and no such script has ever existed (`PLAN.md` Phase 11 Step A removed it). `PLAN.md` §9.1 lists what a real aarch64 port needs - the exception vectors, the GIC, the page tables, PSCI, the generic timer, a UART - and none of it is written. --- ## Testing Three layers, and the middle one is the gate. ### 1. Host unit tests ```bash cargo test -p mpos --lib --target x86_64-unknown-linux-gnu cargo test -p mpos-xtask --target x86_64-unknown-linux-gnu ``` 491 `#[test]` functions in the kernel plus 25 in `xtask`. They cover the arch-independent core: the buddy allocator and the slab heap, the scheduler's policy and signal state, path resolution and mounting, ext2/ext3/ext4 encoding (including a byte-for-byte read of a large file and a mount of an image the host's `mke2fs` made), the ext4 block cache's hit counts, ramfs chunking and its byte budget, pipe rings, the epoll readiness rules, the evdev window's client accounting, inotify's coalescing, the file-lock table, the keymap, the calendar, the shebang parser and the ABI constants. > **`--target` is mandatory.** The root `.cargo/config.toml` pins `[build] target = > "x86_64-unknown-none"`, so a bare `cargo test` tries to run the tests on bare metal. ### 2. The self-test sweep — the gate ```bash cargo xtask test # needs no root ``` It builds the two user test crates, packs them into a throwaway initramfs with a generated `/init`, boots it headless, and parses the serial log: ``` === RUN t-filelock === PASS t-filelock === SUMMARY pass=120 fail=0 === END ``` A program that hangs, crashes or never prints `=== PASS` counts as a failure, and the run must end with the summary line *and* QEMU's clean debug exit (code 33). `MPOS_TEST_TIMEOUT` overrides the 900-second budget. Which programs are booted is `tests.toml`'s answer, not the `/init`'s: the harness stages the selected binaries into `/bin` and the guest runs what it was given. A test in no group always runs; the shipped groups are `external` (the host's network, or a listener on the host), `slow` (measurement, or seconds of work) and `diagnostic` (not a check at all), and `cargo xtask test --groups ` / `--all-groups` runs them on purpose. The sweep prints what it left out, group by group, so a smaller number is never mistaken for a smaller suite. ### 3. The user test programs ~120 programs, split by what each one needs: | Group | Where | Built as | What it pins | |-------|-------|----------|--------------| | **Pure syscall** | `user/abitest/src/bin/syscall/` (86) | `no_std`, raw `syscall`, static `ET_EXEC` | One syscall family's ABI contract on self-contained resources: the success shape, every errno it claims, and the edge cases the man page fixes | | **Integration** | `user/abitest/src/bin/integration/` (22) | `no_std`, raw `syscall` | A device, the network, another process image or a subsystem end to end — plus the timing instruments (`t-forkcost`, `t-pipecost`, `t-netlat`) | | **std side** | `user/tests/src/bin/` (12) | static musl + `rustix` | The surface a real musl program speaks, for the behaviour easiest to pin from a hosted language: sockets, name lookup, spawning | The `no_std` programs take their numbers from the kernel's own `nr.rs` through `#[path = "../../../lib/lin.rs"]`, so a wrong number is a build failure rather than a wrong test. The `std` tests use `rustix` rather than inline assembly, deliberately: a hand-written `syscall` instruction can pass against a kernel that is wrong, because the errno is whatever the register happened to hold. ### 4. Fuzzing `fuzz/` is a separate workspace with 18 libFuzzer targets over the parsers that decide what the kernel trusts from bytes: ELF, ustar, ext2/ext4 on-disk structures, partition tables, VFS paths, the console's escape parser, the keymap, PCI BAR sizing, the scheduler's policy and the input protocols. ```bash cd fuzz cargo fuzz run ext4_image cargo fuzz list ``` --- ## Project layout ``` mpos/ ├── src/ # the kernel crate: lib.rs (host-testable) + main.rs (boot entry) │ ├── lib.rs # library root — what the host build compiles │ ├── main.rs # _start → kernel_main → milestone checks → init handoff → park │ ├── console.rs # log ring, serial output, the move onto the framebuffer │ ├── binfmt.rs # shebang (#!) interpreter │ ├── date.rs # civil calendar arithmetic │ ├── evdev.rs # evdev record window and per-client positions (host-testable) │ ├── uinput.rs # uinput's interface: requests and bitmaps (host-testable) │ ├── pci.rs # the PCI device model and Linux's spellings (host-testable) │ ├── rand.rs # ChaCha20 PRNG: /dev/urandom, getrandom │ ├── uevent.rs # kobject events and the netlink wire format │ ├── logging.rs # the tracing subscriber that writes to COM1 │ ├── sync/mod.rs # the locking doctrine (critical_section, never spin) │ │ │ ├── arch/x86_64/ # machine-dependent half │ │ ├── context.rs # task context switch, lazy FPU (CR0.TS + #NM) │ │ ├── critical.rs # critical_section (cli/sti) │ │ ├── gdt.rs # GDT + TSS │ │ ├── idt.rs # interrupt handlers (timer, #BP, #PF, #NM, syscall, MSI-X) │ │ ├── lapic.rs # local APIC (EOI, MSI-X vector programming) │ │ ├── paging.rs # 4-level page tables, W^X, COW, AddressSpace │ │ ├── pic.rs # 8259 PIC (IRQ0→32, IRQ4→36) │ │ ├── rtc.rs # CMOS RTC as the wall-clock anchor │ │ ├── syscall.rs # the syscall/sysret gate │ │ └── timer.rs # PIT at 250 Hz + TSC sub-tick reading │ │ │ ├── mm/ # memory management │ │ ├── pframe.rs # buddy frame allocator (MAX_ORDER 12) │ │ ├── heap.rs # slab kernel heap (16–2048 B) │ │ ├── vm.rs # mapping plans, KernelSections │ │ └── elf.rs # ELF64 loader (ET_EXEC and ET_DYN + relocations) │ │ │ ├── sched/ # tasks │ │ ├── policy.rs # priority bands, round-robin, sleep queue │ │ ├── runtime.rs # task table (64 slots), switch, fork/exec/wait, teardown │ │ ├── signal.rs # signal numbers, pending bitmask, dispositions │ │ └── futex.rs # futex wait/wake │ │ │ ├── vfs/ # the virtual filesystem │ │ ├── mod.rs # Inode/FileSystem traits, NodeKind │ │ ├── mount.rs # Vfs, mount table, path resolution, pivot_root │ │ ├── path.rs # path parsing │ │ ├── file.rs # fd table, CLOEXEC, RLIMIT_NOFILE │ │ ├── ramfs.rs # ramfs/tmpfs: chunked files, byte budget │ │ ├── ext4/ # ext2/ext3/ext4: super · inode · extent · dir · dx · hash │ │ │ # · alloc · dirent · csum · mapping · cache │ │ ├── procfs.rs # /proc: meminfo, stat, mounts, net, sys knobs, per-pid │ │ ├── sysfs.rs # /sys: devices, class, drm, input, uevents │ │ ├── devfs.rs # /dev node kind and request routing │ │ │ # (the standard node set lives in drivers/devices.rs: │ │ │ # null, zero, tty, console, fb0, dri, rtc0, dsp, hvc0, …) │ │ ├── devpts.rs · pty.rs · tty.rs · termios.rs # terminals │ │ ├── term/ # the terminal emulator: grid · blit · font · keymap │ │ ├── vcmux.rs # the virtual-console multiplexer │ │ ├── pipe.rs # pipes and socketpairs (ring buffer, SCM handover) │ │ ├── epoll.rs · timerfd.rs · eventfd.rs · signalfd.rs · inotify.rs │ │ ├── memfd.rs · dmabuf.rs # anonymous and exported buffers │ │ ├── locks.rs # POSIX record locks and flock │ │ ├── p9.rs # the 9p client (host share) │ │ └── tar.rs # ustar unpacker for the initramfs │ │ │ ├── net/ # networking │ │ ├── mod.rs # smoltcp stack, registry, wakeups, netlink write side │ │ ├── socket.rs # AF_INET / AF_NETLINK socket syscalls │ │ ├── unix.rs # AF_UNIX: path sockets, SCM_RIGHTS, SO_PEERCRED │ │ ├── rtnl.rs # rtnetlink: addr/route/link, applied live │ │ └── vsock.rs # AF_VSOCK │ │ │ ├── drivers/ # devices │ │ ├── pci.rs # PCI config space (mechanism 1), enumeration │ │ ├── devices.rs # device registry, uevent publishing │ │ ├── serial.rs # COM1: TX ring, interrupt-driven RX │ │ ├── rtc_dev.rs # /dev/rtc0 (the node; the clock is arch::x86_64::rtc) │ │ ├── qemu.rs # the isa-debug-exit test port │ │ ├── drm.rs # /dev/dri/card0: the Linux DRM/KMS UAPI │ │ ├── uinput.rs # /dev/uinput │ │ ├── video/ # the render task and the present engine │ │ └── virtio/ # hal · blk · net · gpu · input · console · rng │ │ # · sound · fs9p · vsock · msix │ │ │ ├── syscall/ # the ABI │ │ ├── mod.rs # error codes, user-buffer validation, trace ring │ │ └── linux/ # mod (dispatch) · nr (numbers) · abi · fs · proc · posix │ │ │ ├── blk/ # BlockDev trait, GPT/MBR partition scanning │ └── boot/ # the handoff record the loader leaves behind │ ├── boot/ # mpos-boot: the UEFI loader (x86_64-unknown-uefi) │ └── src/ # main (the ordered sequence) · elf · vm · handoff │ ├── user/ # programs that run on mpos │ ├── src/bin/ # ordinary std programs shipped in the image (hello.rs) │ ├── tests/ # mpos-user-tests: the std-side test programs (musl) │ ├── abitest/ # mpos-user-abi: the no_std ABI conformance suite │ │ ├── lib/lin.rs # the harness: syscall stubs, verdict protocol │ │ └── src/bin/ # syscall/ (86) · integration/ (22) │ └── staging/ # scratch sources, not wired into any build │ ├── xtask/ # the build/run/test front-end (see xtask/README.md) ├── fuzz/ # libFuzzer targets (its own workspace) ├── arch/x86_64/linker.ld # the kernel linker script ├── rootfs/ # files overlaid into the Alpine root filesystem ├── rootfs.toml # what the guest image contains ├── machine.toml # what that image is booted on (host resources, run mode) ├── tests.toml # which user tests the default sweep runs (and which it leaves out) ├── initramfs/ # files overlaid into the early userspace (init lives here) ├── initramfs.toml # what the early userspace contains ├── .cargo/config.toml # targets, rustflags and the `cargo xtask` alias ├── rust-toolchain.toml # nightly + the four targets ├── docs/ # ARCHITECTURE.md · PLAN.md · UAPI.md · README.zh.md ├── README.md · AGENTS.md └── LICENSE ``` `docs/ARCHITECTURE.md` has the same tree as a dependency graph, with the measured edges between layers; `AGENTS.md` is the short version for anyone (or anything) about to make a change. --- ## Design principles - **Library/binary split.** The crate is a library so the host can test it. Machine code is gated behind `target_os = "none"`; pure logic — arithmetic, encodings, parsers, policies — stays on the host side even when its only caller is bare metal. - **One syscall table.** The test suite includes `src/syscall/linux/nr.rs` with `#[path]`, so kernel and tests cannot drift apart on a number without a build failure. - **Uniprocessor simplicity.** All shared state is `critical_section::Mutex>` — interrupts disabled *is* mutual exclusion. `spin::Mutex` is deliberately never used: busy-waiting on a lock held by a task this core just preempted would deadlock. Nothing sleeps under a lock. - **Every wake is an event, not a tick.** A blocking call parks on the thing it is waiting for and is woken by the arrival, not by the next clock interrupt. This single discipline is what took loopback `ping` from 7.5 ms to 0.3 ms and a two-process pipe handover from 7 MiB/s to 10 168 MiB/s (`PLAN.md` §10.1, §10.4). - **Nothing is claimed without a number.** Performance changes in `PLAN.md` carry before/after measurements from an instrument that runs in the suite — `t-forkcost`, `t-pipecost`, `t-netlat`, or a host test that counts device reads. - **Divergences are recorded, not hidden.** Where MPOS deliberately differs from Linux, the reason and the test that pins it are written down next to the feature. ### Behaviour worth knowing | Behaviour | Note | |---|---| | **COW fork** | Shared pages are marked read-only with a COW bit; the `#PF` handler copies on first write. Frame reference counts live in the buddy allocator | | **Demand-paged stack** | Starts at 16 pages and grows on fault, bounded by `RLIMIT_STACK` with `STACK_GROWTH_SLACK` for the check | | **Write-through ext4** | Every `write_at()` reaches the device. No journal, no dirty tracking, no background writeback: simple, and crash-visible by construction (the image is built `^has_journal,^metadata_csum` for the same reason) | | **Signals** | Delivered through a real `rt_sigframe`, so a handler is called and `rt_sigreturn` resumes the interrupted code. `SIGKILL` and `SIGSTOP` cannot be blocked | | **ASLR** | An `ET_DYN` image, its interpreter, the stack top and the `mmap` region are all drawn per `exec`; an `ET_EXEC` image keeps the address it was linked at, because that is what a fixed address means | | **Slab allocation and large requests** | Requests over 2048 B bypass the slab and take frames directly; the heap keeps freed slots cached, which is why "free" memory in `/proc/meminfo` is not the whole story | | **Kernel panics end the machine** | `panic = "abort"` and a panic handler that dumps a stack scan and exits QEMU with `35`. The global allocator's failure path is still a panic — an open item in `PLAN.md` §8.0 | --- ## Documentation | File | What it is | |---|---| | `README.md` | This file: what MPOS is and how to build, run and test it | | `AGENTS.md` | The working guide: commands, conventions, recipes for adding a syscall or a test, known traps | | `docs/README.zh.md` | Chinese translation, kept in section-by-section correspondence with this file | | `docs/ARCHITECTURE.md` | The dependency graphs, the boot and syscall sequences, per-layer ownership, the measured inter-module edges, and the layering violations | | `docs/PLAN.md` | The feature roadmap and status board, the measured performance work, and Phase 11 — the architecture-debt audit and its remediation plan | | `docs/UAPI.md` | The userspace-visible surface as an inventory: syscalls, device ioctls, struct layouts and flag vocabularies, each with Linux's answer, this kernel's, and the pin — the working list of what is served, honestly refused, or a wrong shape | | `xtask/README.md` | Every build/run command, flag and environment variable (bilingual) | | `fuzz/README.md` | What each fuzz target feeds on | --- ## License This project is licensed under the [MIT License](LICENSE). ---
Built with Rust — proven correct, one milestone at a time.