| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A 64-bit x86 teaching kernel that carries its own toolchain. You can write a C program inside the running system, compile it, link it and execute it without leaving the machine.
I have implemented a primitive desktop environment within MiniOS that operates independently of the serial console. This subsystem provides a graphical user interface (GUI) with window management, mouse support, and scrollable terminal emulation.
The desktop now supports PNG icons (via stb_image) that can be placed and launched from the graphical shell. Immediate-mode UI is powered by Nuklear, giving windows, buttons and widgets without a retained-mode toolkit. A low-code tool lets you author CVM modules in a simplified form that compile straight to bytecode and run natively on the CVM (with the existing JIT). Additional support includes xxHash for fast hashing and experimental TFT display output alongside the VESA framebuffer.
miniOS> edit src/p.c
edit> a
int main(void) { return 7; }
edit> x
miniOS> run objects/minigcc.o src/p.c > asm/p.s
miniOS> run objects/ld.o -f elf -o bin/p.elf asm/p.s
miniOS> run objects/ld.o -f cvm -o cvm/p.cvm asm/p.s
miniOS> run bin/p.elf
exit code: 7
miniOS> run cvm/p.cvm
exit code: 7
miniOS>
The bottom taskbar is a live status strip, not a hint line:
Alt is the WM modifier. From the desktop:
| Shortcut | Action |
|---|---|
| Alt+Enter / F11 | toggle fullscreen |
| Alt+Arrow keys | snap the window to a screen half |
| Alt+Home / Alt+End | snap to the top-left / bottom-right quadrant |
| Alt+[ / Alt+] | shrink / grow width |
| Alt+- / Alt+= | shrink / grow both dimensions |
| Alt+0 / F5 | reset the window to its default position (and size) |
| Ctrl+Arrow keys | nudge the window by one cell |
The window keeps its size across redraws, so a snap or resize persists instead of snapping back to the default.
The shell runs in a titled, movable window on the desktop, not on the whole screen:
Moving, snapping and resizing never lose the current screen: the prompt and any typed or echoed text are re-rendered from the logical buffer at the new position and size.
MiniOS is one of four projects that together make up the system. This repository holds the kernel, the boot path and the ramdisk; the toolchain it carries lives next door.
| Repository | Role |
|---|---|
| miniOS | this repository: kernel, two-stage boot path, ramdisk, shell, editor |
| miniGCC | C compiler: C to x86-64 AT&T assembly |
| ld | assembler and linker: assembly to a Linux ELF or a CVM module |
| cvm | the CVM / cvm2 bytecode interpreter |
The ramdisk ships prebuilt objects in progs/, so make produces a bootable image with nothing else installed. To build the whole system from source instead, clone the other three next to this one:
make sources # clone the missing repositories from GitHub
make toolchain # build minigcc, ld and cvm2 from those sources
make # rebuild every ramdisk object and os.imgmake sources never touches a directory that already exists, so a checkout with local work is left alone. make sources-update pulls the latest commit of each before rebuilding, and make sources-status shows which revision each one is sitting on.
Expected layout — the directory holding this repository can have any name:
src/ ├── miniOS/ (this repository) ├── miniGCC/ ├── ld/ └── cvm/ with cvm/cvm2 inside
Point the build somewhere else with MINIGCC_DIR=, LD_DIR=, CVM_REPO_DIR= or CVM_DIR=; change where make sources clones from with MINIGCC_URL=, LD_URL= or CVM_URL=.
Everything on the ramdisk is regenerated from source by make: objects/minigcc.o, objects/ld.o and objects/cvm.o are compiled from the sibling checkouts, and the demo programs (fib, w1, minigcc) are compiled from this repository's own C sources in progs/src/ through the full miniGCC-to-ld chain. Nothing in the image is a binary you have to take on trust.
The compiler on the ramdisk is self-hosted: bin/minigcc.elf was compiled by minigcc itself (generation 3) and linked by ld (no GNU as/ld anywhere after generation 1), and make selfhost verifies the bootstrap fixed point on the host. Inside the OS the self-hosted compiler drives the same edit/compile/ link/run loop as objects/minigcc.o.
make # builds os.img
make run # boots it in QEMU with a display
make serial # boots it headless on the serial console
make test # behavioural suite (QEMU + serial console)
make selfhost # compile minigcc with minigcc, link with ld, check fixed pointThe image is attached as an IDE disk. The boot path uses INT 13h extended (LBA) reads, which floppy emulation does not provide.
The kernel owns an rtl8139 NIC under QEMU user networking (slirp): Ethernet, ARP, IPv4, ICMP echo, UDP, DNS and a client TCP (SYN handshake, stop-and-wait with retransmissions, FIN teardown). net shows the counters and net ping 10.0.2.2 sends one ICMP echo.
On top of TCP the kernel speaks TLS 1.2 as a client (tls_handshake/tls_send/tls_recv, MiniOS syscalls 201-203): ECDHE-RSA/ECDSA with AES-128-GCM, certificate chains verified down to 8 embedded public roots, hostnames checked against SAN or CN with single-label wildcards. Real-world browsing works from the shell:
miniOS> run bin/freedom https://duckduckgo.com miniOS> run bin/freedom mini os kernel # DuckDuckGo search over https miniOS> run bin/freedom https://en.wikipedia.org/wiki/Mini miniOS> run bin/freedom --dump-dom https://example.com miniOS> run bin/freedom --dump-css https://example.com
bin/freedom is the headless text browser: a curlfree-style HTTP engine with a FreeDom-style omnibox (an argument that is not a URL is a DuckDuckGo search; bare hosts are fetched as https://), redirect chasing, chunked decoding, an HTML-to-text filter, and --dump-css/--dump-dom headless dumps. The crypto and the roots are host-tested by make test-tls (fixed vectors plus full TLS 1.2 handshakes against OpenSSL-driven servers, including the negative set).
| Command | Purpose |
|---|---|
| help | command summary |
| ls / cat <file> / echo <text> | ramdisk browsing |
| edit <file> | line editor |
| load <file> | load an ELF (.o relocatable, or a Linux executable) |
| run <name|file> [args] | run a program, an ELF or a .cvm module |
| <cmd> [args] | run an ELF from bin/<cmd>: the Linux-style command path |
| <cmd> > <file> | redirect command output to a ramdisk file |
| date | print the CMOS clock (HH:MM:SS), the same clock the taskbar shows |
| vol [0-100] | print the PC-speaker volume; with an argument, set it |
| net / net ping <ip> | network status and one ICMP echo |
| clear / poweroff | console and power |
Redirection captures what the command writes, not what the shell reports about it, so run objects/minigcc.o p.c > asm/p.s yields assembly a linker can consume.
bin/cp is a command-path utility: cp src/fib.c x.txt copies a ramdisk file without run or load. It is compiled from this repository's own progs/src/cp.c through the miniGCC-to-ld chain, and the source ships on the ramdisk as src/cp.c, so the utility can be rebuilt inside the OS by the OS.
bin/lzss and bin/unlzss are the Okumura LZSS (de)compression tools, both built from a single progs/src/lzss.c: lzss rep.txt rep.lzs compresses, unlzss rep.lzs rep.out decompresses, and the binary picks its mode from argv[0] (unlzss decodes; -d forces decode). The on-disk format is a fail-closed LZS1 magic plus the original size; decoding rejects a bad magic, a truncated stream and any declared size beyond the expansion bound derived from the input length, so a hostile header can never drive an oversized allocation.
bin/lz4 and bin/unlz4 are the LZ4 (de)compression tools, also built from a single progs/src/lz4.c: lz4 rep.txt rep.lz4 compresses and unlz4 rep.lz4 rep.out decompresses, with the same argv[0]/-d dispatch as lzss. The codec lives in the kernel (lz4_kernel.c, the same one MiniFS uses), so the tools are thin front-ends over the MiniOS syscalls 216/217 and the on-disk block is exactly the MiniFS LZ4 block format: a 4-byte little-endian original size followed by the raw LZ4 stream, so lz4 output interops with the filesystem's own blocks.
bin/aes and bin/unaes are AES-256-CTR file encryption tools built from a single progs/src/aes.c, shipped on MiniFS like DOOM and MicroPython: aes <key-hex64> <nonce-hex32> <src> <dst> encrypts and the matching unaes ... decrypts. The S-box is generated procedurally from the GF(2^8) inverse plus the FIPS-197 affine transform (no magic tables), the mode is CTR with no padding, and the fail-closed AES1 container detects bad magic, truncation and size tampering. CTR gives confidentiality only - pair it with a MAC if you need integrity.
edit <file> opens a line editor over a ramdisk file.
| Key | Action |
|---|---|
| h | help |
| l | list the buffer |
| p N | print line N |
| e N | replace line N with the next line typed |
| a | append the next line typed |
| i N | insert before line N |
| d N | delete line N |
| w | save |
| x | save and quit |
| q | quit, refusing to discard unsaved changes |
| q! | quit, discarding changes |
A file too large for the buffer is loaded read-only: the editor refuses to write it back rather than silently dropping the part it never read.
MiniOS runs three kinds of program:
Every .cvm module is compiled to native x86-64 code at load time by a baseline JIT. The JIT is transparent: output is byte-identical to the interpreter, and the full miniGCC compiler (minigcc.cvm) runs correctly under JIT inside the OS.
The JIT compiles each function independently into a native code buffer on the kernel heap (executable via 2 MB pages). Cross-function control flow (CALL/RET) uses the interpreter's frame stack, so the ABI is unchanged. If JIT initialization fails, the interpreter takes over transparently.
On the host the JIT buffer uses mmap (RWX); inside MiniOS it uses malloc (the kernel heap is already executable). Source files: cvm_jit.c (compiler), cvm_jit_x86.c (x86-64 emitter), cvm_jit_help.c (runtime helpers).
The ramdisk ships organized by kind, and the shell's ls <dir> lists each directory:
| Directory | Contents |
|---|---|
| objects/ | ET_REL toolchain: minigcc.o, ld.o, cvm.o, demo .o |
| bin/ | Linux ELFs + command-path utilities (cp, freedom, micropython) |
| cvm/ | CVM modules: fib.cvm, w1.cvm, minigcc.cvm |
| src/ | C sources for every program on the ramdisk |
| asm/ | miniGCC assembly (*.s) for the toolchain-built programs |
| docs/ | HTML and other documentation fixtures |
The ramdisk is flat — the / in a name is data, and mkramdisk.py derives each name from the path relative to progs/.
MiniOS ships a doomgeneric port that runs DOOM as a static Linux ELF at ring 3. The engine compiles from progs/doomgeneric/ with the platform layer in doomgeneric_minios.c. The desktop runs on a VESA linear framebuffer (800x600x8 by default, falling back to 640x480 and Mode 13h); DOOM renders its 320x200 frame into a kernel back-buffer and the kernel composites it onto the desktop in a titled window (SYS_DOOM_FRAME, 211), centered on the screen, so the shell window stays visible while you play. The shareware WAD (doom1.wad) is bundled on the minifs.
miniOS> run doomgeneric.elf
| Key | Action |
|---|---|
| WASD | move |
| Ctrl | fire |
| Space | use / open |
| Arrow keys | turn / strafe |
| 1-7 | weapon select |
| Shift | run |
| Esc | menu |
Sound effects play through the QEMU PC speaker: the kernel drives PIT channel 2 (ports 0x42/0x43) plus the gate bit on port 0x61, and the sound module in i_minios_sound.c maps each DP lump (1-byte frequency index + 1-byte duration in 70 Hz ticks, after a 2-byte priority) through the original Doom PC-speaker frequency table. QEMU must wire the PC speaker to the audio backend — -machine pc,pcspk-audiodev=<id> in addition to -audiodev <backend>,id=<id> (the run target sets both via QEMU_AUDIO). A bare -audiodev alone routes nothing, so the beeps are silent without the machine option.
The level music also plays on the same PC speaker with NES-style pseudo-polyphony. A music_pcspeaker_module in i_minios_sound.c decodes each MUS lump (the Doom music format, D_E1M1 etc. at the stock 140 ticks/sec) straight from its interleaved event stream, splits the sounding notes the way a NES split its voices: the lowest bass note becomes a sustained pedal (the triangle voice) while only the top few melody notes are fast-arpeggiated round-robin, holding each for 7 ms. The ear hears a strummed chord with a solid bass foundation instead of every voice chopped at equal length — the chiptune broke-chord trick, applied so dense arrangements stay clear. The module is picked when snd_musicdevice is the PC speaker, and S_UpdateSounds was re-enabled in d_main.c so both the sfx note sequencer and the music decoder are advanced each frame (they were previously never polled).
The binary is built with the host toolchain (static, no-pie) and placed at bin/doomgeneric.elf on the minifs. To rebuild from source:
make doomgeneric.elf # or just `make` to rebuild everythingThe kernel provides four custom syscalls for the port: time_ms (204), kbd (205), palette (206) and kbd_raw_mode (207). VGA Mode 13h is entered through sys_vga_mode (208), which tells the kernel to stop touching VGA text hardware while the game runs.
MiniOS ships MicroPython as a static Linux ELF at ring 3, built from the upstream unix port with a custom MiniOS variant. The variant enables floats, the compiler, the os module and computed-goto, and disables readline (the kernel handles echo and line editing), sockets, threading, SSL, FFI, termios and native emitters. The binary is linked with gcc -static -no-pie, exactly like DOOM.
miniOS> micropython -c "print(6 * 7)" 42 miniOS> micropython -c "print(1.5 * 2)" 3.0 miniOS> micropython src/hello.py hello from python miniOS> micropython >>> print(40 + 2) 42 >>> exit() miniOS>
MicroPython resolves through the command path (bin/micropython) like cp and freedom, so both micropython and run micropython.elf work. Scripts are opened through the unified filesystem (ramdisk first, MiniFS fallback), and the interactive REPL reads from the serial console.
The kernel provides several syscalls for glibc-static compatibility: getcwd (79) returns the shell working directory, newfstatat (262) reports S_IFREG/S_IFDIR with file sizes from the unified filesystem, and readlink (89) returns EINVAL (MiniOS has no symlinks) so glibc's realpath() treats every path as a regular file and keeps resolving. A script's realpath() and directory traversal work without needing a full VFS layer.
MicroPython previously crashed on exit (EXCEPTION 14). The root cause was that k_exec_user did iretq to the ELF entry without zeroing the initial registers, unlike Linux. glibc's _start does mov %rdx,%r9 to obtain rtld_fini; the leftover kernel value in rdx was a base-less function pointer, which __libc_start_main registered as an exit handler and then __run_exit_handlers demangled and called on exit — a wild jump. The fix zeroes rdi, rsi and rdx before iretq, so rtld_fini is NULL and every glibc binary exits cleanly.
MicroPython ships with a minios C module (progs/micropython/variants/minios/minios_module.c) that exposes kernel services: time_ms(), rtc(), fb_info(), vol(), pal(), pcspeaker() and run(). run(path, args, redirect) invokes the kernel SYS_SPAWN (215) boundary, which runs a ramdisk program from the interpreter while preserving it, so scripts can chain toolchain commands. Three scripts on the ramdisk use this:
miniOS> micropython src/build.py # minigcc -> ld -> run, every target miniOS> micropython src/shell.py # pybash: variables, capture, run miniOS> micropython src/test.py # in-OS test suite (kernel + toolchain)
build.py orchestrates the self-hosted toolchain (ET_REL minigcc.o and ld.o run at ring 0 via SYS_SPAWN and work), shell.py is a Python shell layer with variables and output capture, and test.py verifies the kernel bindings and the toolchain from inside the machine.
Build from source:
make sources # clones micropython if missing
make # builds mpy-cross, the unix port, and packs the ELFThe variant files live in progs/micropython/variants/minios/; the build runs entirely on the host and copies the resulting ELF into progs/bin/.
User-mode binaries (ET_EXEC / ET_DYN) run at ring 3 with hardware no-execute (NX) page protection. The kernel builds eager 4 KB page tables for the whole user window and sets EFER.NXE at boot; every user page starts non-executable and load_exec_elf clears NX only on the pages a program's executable segments occupy. A program cannot execute from its stack, heap or .data — a jump into a non-executable page faults and the machine resets, never silently running shellcode (proven by the nx.elf probe in the BDD suite). The kernel heap keeps its 2 MB executable pages, because the .o toolchain programs execute from there at ring 0 by contract.
The kernel image's physical base is randomized per boot (KASLR). Stage 2 mixes the TSC with the CMOS clock (hours, minutes, seconds fed into separate bytes) and slides the kernel into one of 64 aligned 2 MB slots in [0x6000000, 0xE000000). The kernel always executes at virtual 0x100000; the boot banner reports its randomized physical base. Disable with make ENABLE_KASLR=0 for deterministic physical layout.
A ring of 4096 lines that scrolled off the top of the 25-row VGA screen. Captured lazily from vga_scroll() and viewable with PageUp/PageDown.
| File | Role |
|---|---|
| bootdefs.h | every constant shared by the boot path |
| stage1.S | 512-byte boot sector: loads stage 2 over LBA |
| stage2.S | loads the kernel above 1 MB, enters long mode |
| kernel.c / kernel.h | kernel: console, heap, ramdisk, loaders, shell, editor |
| net.c / net.h | rtl8139 driver, ARP/IP/ICMP/UDP/DNS/TCP |
| tls.c / tls_crypto.c / tls_x509.c | kernel TLS 1.2 client, crypto, X.509 |
| tls_roots_src/ + mkroots.sh | the 8 embedded CA roots and their generator |
| tls_test.py / tls_test.c | host TLS suite: vectors + full handshakes |
| cvm_host.c | CVM interpreter + JIT integration in MiniOS |
| progs/ | ramdisk contents organized by kind: objects/, bin/, cvm/, src/, asm/, docs/ |
| mkramdisk.py | packs progs/ into the ramdisk image |
| test_bdd.sh / test_http_server.py | behavioural suite and its HTTP fixture |
| mcp/minios_mcp.py | MCP bridge: boots the OS and exposes its console as tools |
| mcp/test_minios_mcp.py | unit + QEMU BDD suite for the bridge |
| mcp/mutate_mcp.sh | mutation testing for the bridge |
| skills/minios/SKILL.md | agent skill: the edit/compile/link/run workflow over the bridge |
| mutate.sh | mutation testing |
mcp/minios_mcp.py exposes a running MiniOS as MCP tools: minios_boot, minios_status, minios_send, minios_expect, minios_snapshot, minios_write, minios_cat, minios_poweroff. On top of the shell it adds minios_python (run a ramdisk .py script with MicroPython) and minios_py_eval (evaluate a one-liner). The server owns the QEMU child and a pty-backed serial console; the companion skill (skills/minios/SKILL.md) teaches the edit/compile/link/run workflow, so an agent can write a C program inside the OS, build it with objects/minigcc.o and objects/ld.o, run it and read exit code: N, or drive the in-OS Python toolchain (build.py, shell.py, test.py), all without leaving the machine.
python3 -m unittest -v mcp/test_minios_mcp.py # unit + QEMU BDD (skips without QEMU)
mcp/mutate_mcp.sh # every bridge mutant must dieThe bridge is driven over stdio JSON-RPC, uses only the Python standard library, validates every input before a byte reaches the console (path whitelist, printable ASCII, editor line and buffer limits), and never leaks a QEMU process: the pid file under the system temp dir reaps stale instances and every exit path terminates the child. See CLAUDE.md for the full contract.
| Target | Purpose |
|---|---|
| all (default) | build os.img |
| sources | clone the missing toolchain repositories from GitHub |
| sources-update | pull the latest commit of each one |
| sources-status | show the revision each checkout is on |
| toolchain | build minigcc, ld and cvm2 from those sources |
| selfhost | compile minigcc with minigcc, link with ld, verify the bootstrap fixed point |
| test-tls | host TLS suite: crypto vectors + full handshakes |
| run / serial / debug | boot the image in QEMU |
| test | behavioural suite |
| clean | remove every build product |
See CLAUDE.md for the full engineering contract.
This project has been analyzed by ReadMenator, a zero-token polyglot static analysis tool. A comprehensive knowledge base is available:
AI agents and developers: Read KNOWLEDGE_BASE.md for full project context without LLM token cost.
| Back | FazBrowse Home | New Git URL |