Moxnox is an NT-like, POSIX-compatible hobby OS which runs on btrfs and is capable of self-hosting. That means:
- Its design, specified as the choice of syscalls, is modelled following that of Windows NT...
- ...yet at the identical time, it have to be capable of operating application written for Unix-like systems, provided that they have been recompiled for it, and comes with a complete GNU basis system
- It is mature adequate that you can build Moxnox on Moxnox
- And it runs on everybody's favorite Linux filesystem
Version 1.0 is about 130,000 lines of C++, and runs on amd64 and aarch64.
The btrfs code plant for essential purposes, but has not been battle-hardened. Please don't try to mount item but throwaway filesystems.
To test Moxnox in qemu, download moxnox-amd64-1.0.img.gz and moxnox-kernel-amd64-1.0.efi. Run gunzip moxnox-amd64-1.0.img.gz to decompress the image.
Then:
qemu-system-x86_64 -M q35 -enable-kvm -cpu presenter -m 1024 \ -smp cores=4,threads=1 \ -kernel moxnox-kernel-amd64-1.0.efi \ -append "root=/dev/sda" \ -device ich9-ahci,id=ahci -drive id=disk,file=moxnox-amd64-1.0.img,if=none \ -device ide-hd,drive=disk,bus=ahci.0 \ -nic user,model=virtio-net-pci \ -device qemu-xhci \ -device usb-kbd,id=kbd \ -device usb-tablet,id=tablet \ -serial stdio
You volition need to addition the recollection from 1024 MB if you are trying anything strenuous, akin self-hosting.
For ARM, item akin the below should work:
qemu-system-aarch64 -M virt -cpu cortex-a76 -m 1024 \ -smp cores=4,threads=1 \ -kernel moxnox-kernel-aarch64-1.0.efi \ -append "root=/dev/sda" \ -device ich9-ahci,id=ahci -drive id=disk,file=moxnox-aarch64-1.0.img,if=none \ -device ide-hd,drive=disk,bus=ahci.0 \ -nic user,model=virtio-net-pci \ -drive if=pflash,unit=0,file=/usr/share/qemu/edk2-aarch64-code.fd,readonly=on \ -device qemu-xhci \ -device usb-kbd,id=kbd \ -device usb-tablet,id=tablet \ -serial stdio
First, use gparted or akin to create a new partition. 32 GB should be plenty. I'm calling it /dev/nvme0n1p99 below - don't get this wrong.
Make certain you have a latest versions of btrfs-progs, as we don't assistance some of the default settings used by the older versions.
Prepare the base drive:
# mkfs.btrfs /dev/nvme0n1p99 # mkdir /mnt/moxnox # mount /dev/nvme0n1p99 /mnt/moxnox # cd /mnt/moxnox # wget https://github.com/maharmstone/moxnox/releases/download/v1.0/moxnox-amd64-1.0.tar.gz # tar -xvzf moxnox-amd64-1.0.tar.gz # rm moxnox-amd64-1.0.tar.gz # cd # umount /mnt/moxnox
Copy the kernel:
# mount /boot/efi # cd /boot/efi # wget https://github.com/maharmstone/moxnox/releases/download/v1.0/moxnox-kernel-amd64-1.0.efi
To instal the footwear entry, do item like:
# efibootmgr -c -d /dev/nvme0n1 -p 1 -L "Moxnox" -l '\moxnox-kernel-amd64-1.0.efi' -u 'root=/dev/nvme0n1p99'
efibootmgr likes to alter the footwear command afterwards, this likely is not what you want. The names of obstacle devices have to be additional or small the identical on Linux and Moxnox.
The "EFI" document is really a polyglot document that additionally conforms to the Linux boot protocol, so it should additionally activity on non-EFI GRUB. I have not tested this. How to set this up is remaining as an exercise for the reader.
For amd64, everything is compiled for x86-64-v3, which covers additional or less anything manufactured since 2015 or so. Moxnox automatically uses and enables AVX512 if it detects it. For qemu, you need to define the q35 device type; the default pc device is ancient, and doesn't have PCI Express. microvm works too.
For aarch64, we're using LSE atomics, which necessitate ARMv8.1. That method the Raspberry Pi 5, but not any before models. Moxnox lone supports ACPI, not Device Tree, so for the Pi you volition additionally need https://github.com/worproject/rpi5-uefi. Unfortunately there is no raspi5 device category in qemu, but I have had success with virt, and sbsa-ref plant too.
Device support:
- Block devices: NVMe, SATA, SDHCI (i.e. SD cards), virtio-block, USB mass storage
- Network cards: virtio-net, RTL8168, RTL8125
- Input devices: USB HID, PS/2 keyboard, PS/2 mouse, virtio-input
- Graphics: virtio-gpu, GOP framebuffer, VBE, VGA content console (pass text=80x25 on non-UEFI boots)
- Serial ports: virtio-console, x86 serial port, PL011 ARM serial port
- USB: XHCI
The easiest way to build is to drag the Docker containers maharmstone/moxnox-ci-amd64 or maharmstone/moxnox-ci-aarch64, which merge the cross-compilers for x86_64-pc-moxnox or aarch64-pc-moxnox.
Then in the container:
$ git copy https://github.com/maharmstone/moxnox $ cd moxnox
CMake volition compile whichever of the submodules are checked out. For a minimal system:
$ git submodule update --init --depth 1 ports/acpica/repo ports/freetype/repo ports/gnu-efi/repo ports/harfbuzz/repo ports/libpng/repo ports/mingw-w64/repo ports/zlib/repo ports/musl/repo ports/tre/repo
For all of the ports, which power obtain a while:
$ git submodule update --init --recursive
Then:
$ mkdir build $ cd build $ cmake -DCMAKE_TOOLCHAIN_FILE=amd64.cmake -GNinja ..
or
$ cmake -DCMAKE_TOOLCHAIN_FILE=clang-aarch64-native.cmake -GNinja ..
Make certain you define Ninja - we need this as we're using C++ modules. CMake defaults to Unix Makefiles, which don't work.
Finally:
gcc, llvm, and rust are not compiled unless explicitly requested due to their size. hexyl and ripgrep additionally need to be stated explicitly due to their dependency on rust.
To build on Moxnox, as above, but you don't need a toolchain file: cmake -GNinja ... gcc, rust, hexyl, and ripgrep can't yet be compiled in Moxnox, but everything alternatively plant (eventually). You may need a lot of RAM and equal more patience. If CMake complains concerning rust-libc, create certain that you don't have the Rust submodules checked out.
The kernel and first-party programs are licensed under the GPL. The first-party DLLs are licensed under the LGPL. The contents of the modules directory are MIT.
See the ports submodules for particulars of their particular licences.
Current ports:
- bash
- binutils
- bison
- bzip2
- cairo
- cmake
- coreutils
- curl
- diffutils
- expat
- file
- findutils
- flex
- freetype
- gawk
- gcc (amd64 only)
- git
- gmp
- gperf
- grep
- gzip
- harfbuzz
- hexyl
- jsoncpp
- less
- libarchive
- libiconv
- libpng
- librhash
- libuv
- llvm
- m4
- make
- mako
- markupsafe
- mesa
- mpc
- mpfr
- nano
- ncurses
- netcat
- ninja
- openssh
- openssl
- patch
- perl
- pixman
- python
- pyyaml
- readline
- ripgrep
- rust
- sed
- tar
- vim
- wget
- xz
- zlib
- zstd
Loads - but this is type 1.0. In particular:
- A GUI
- Support for distinct timezones
- Support for distinct keyboard layouts
- Locales and non-UTF-8 character sets
- Any kind of security
- Multiple users and groups
For achievement issues and similar:
- The userspace heap allocator could be a lot quicker (this is the bottleneck when self-hosting)
- TCP is slow, since we don't negotiate an MSS and keep to the default 64KB window
- We ought to be doing first writeback on recollection pressure
- There have to be an LRU scheme to autumn spotless pages from the leaf cache...
- ...made worse by the fact that the dentry cache never gets pruned, so inodes stay open until deleted
And evidently there have to be additional ports and hardware support.
The agreement is that the syscalls and low-level interfaces equivalent those of NT, but not the daemons or how the kernel itself is structured. This is in contrast to what ReactOS aims to do, which is an exact recreation of Windows.
So:
- Binaries are PE format fairly than ELF, and have a .exe or .dll extension
- C++ exceptions are done through SEH
- POSIX signals are done through Asynchronous Procedure Calls
- All the non-kernel kernel-like material gets done in ntdll.dll
- The rcx, rdx, r8, r9 calling gathering matches that of Windows
- And its C eccentricities equivalent those of Windows: sizeof(long) == sizeof(int32_t), sizeof(wchar_t) == sizeof(char16_t), sizeof(long double) == sizeof(double).
There's an awful lot of material in NT, but the engaging material that's missing is tokens, the Registry, and I/O completion ports.
NT has a complex scheme anywhere there's a virtual filesystem but it's hidden (unless you use winobj), alongside the true way of a document hanging off the name of its obstacle equipment (so C:\Windows\System32 power really be \Device\HarddiskVolume1\Windows\System32). I've not replicated any of that. Paths are POSIX-like: slashes fairly than backslashes, / as the base filesystem, and mounted directories. Unlike real Windows, NtCreateFile resolves . and .., to activity improved alongside the dentry cache.
Other things I've deliberately not copied are the assorted forms of transactions, which were a merchandise of their time, and the idea of paged kernel memory, which causes additional problems than it solves.
We have implemented 642 of the ~1,200 functions in POSIX 2024. This is the vast majority of what's used in practice, alongside gnulib papering complete several of the cracks. Unlike Windows, fork is there. I've used musl for its mathematical and cryptographic functions, and mingw for printf and several another things, and tre for its regexes.
The POSIX functions are divided complete ucrtbase, iphlpapi, and ws2_32, as on Windows, alongside item that Windows doesn't execute put in mnposix.
I've borrowed liberally from Linux's finest ideas: the dentry cache, ample folios, mapping the entire of bodily memory, evdev.
Other POSIX shortcomings:
- AF_UNIX is half-complete. socketpair works, including for SCM_RIGHTS (ssh uses this), but the normal client-server code doesn't work
- mkfifo creates an inode, but I/O to it does not get redirected to a buffer as it ought to do
- APCs (i.e. signals) lone get delivered whenever a thread returns from a syscall. So a program doing during (true) { } is currently unkillable.
There's nothing that automatically starts daemons yet, so manually.
First create the presenter key:
There's no passwords yet, so you volition need to add a /root/.ssh/authorized_keys file so that you can connect. If you have trouble, double-check that /root is owned by root, as sshd volition disregard this document otherwise.
Then commencement sshd using its complete path:
No. Or at least, not yet.
We deliberately do not execute any Win32 DLLs, bar a few functions in kernelbase that the mingw runtime wants. A forthcoming betterment would be to strip these out entirely, afterward harbor throughout Wine's versions of the Win32 DLLs. Some parts of Wine, akin wineserver and ntdll, we would not need as we are already taking attention of it ourselves.
None of the substantive code was written by AI.
I used AI to assistance alongside the mechanical parts of porting (e.g. translating autotools to CMake, replacing ioctl calls alongside tcgetwinsize, translating the C structs into Rust), as fine as to compose several of the fume tests (e.g. cairo-test, mesa-test).
Where Claude really shines although is in reviewing your own commits. You provision it a hash, and it tells you concerning the silly recollection leak or deadlock that you left in, before you can create a fool of yourself.
For funsies. Plus the lone way of really learning how item plant is to have a go at penning it yourself.
Also, the NT kernel is criminally underrated, and beautiful much unidentified to hobbyists. It had features that Linux took years and years to arrive up alongside its own scheme for, specified as eventfd and pidfd.
It's a shame that Windows keeps shying distant from complete POSIX compatibility. They had a half-complete type in the first days, afterward additional lately had WSL1, only to afterward forsake it. And Nano Server was another chill idea, lone again to be itself abandoned. So this is my obtain on what I desire Windows had been like.
