A small (hobby) multiserver microkernel operating system for RISC-V, LoongArch and x86 (i686 and x86_64), using a homemade kernel and userspace, and mlibc as the main C library, partially developed as my end of degree project. The goal of the project is to make a general purpose operating system, with the objective of learning and being suitable for development and exploration of the RISC-V and X86 platforms. The microkernel is mostly written in C++, and the userspace is in a mixture of C, C++ and Rust (and ASM where needed). The limine bootloader and Hyper bootloader are used for booting the system, depending on the architecture, and i686 and x86_64 ports are also bootable with multiboot2, with x86_64 version being triple protocol with the same binary.
RISC-V Execution, running bash in qemu-riscv64 with ns16550 terminal:

Baremetal execution (of an old build) on an old x86 (AMD E450) laptop, discovering an internal SATA HDD:

The OS is trying to follow a multiserver microkernel architecture, running most drivers and servers in userspace. The kernel is in charge of memory management, scheduling, interrupts, and IPC. Capabilities are used for natural permissions management. The POSIX functions are mostly handled by personality servers in userspace, communicating through IPC.
Asynchronous IPC queues are currently used for IPC, with unbounded async send (multi-sender, anyone holding the right to send), and sync receive of messages of arbitrary size (with a single thread receiver). In addition, the kernel exposes memory objects to userspace, which can be used to provide shared memory between processes, and futex for quicker synchronization. The capabilities (rights) can either be transferred over IPC, or be moved between the capability namespaces (kernel process groups).
The system bringup works by first starting the kernel with some boot protocol (limine, multiboot2 or Hyper), which then starts the statically linked bootstrap server, which in turn starts the posix/processd server, which then bringup the rest of the userspace.
The project is roughly structured as follows:
kernel/- The microkernellib/- Different system librariesmlibc/- The mlibc C libraryuserspace/- Different userspace servers/programshost-recipes/,recipes/,patches/,Jinxfile- Jinx build system stuffscripts/- Build support scripts
The Jinx meta-build-system is used for building the system. A Makefile is provided in the root of this project, which downloads Jinx and uses it to build the system images for different architectures. The qemu, qemu-x86, qemu-i686 and qemu-loongarch64 makefile targets are provided to build and run RISC-V, amd64, ia32 and LoongArch64 images in qemu, respectively.
If you would like to use jinx directly, you can run
export source_dir=<path to this repository>
cd ${source_dir} && git submodule update --init --recursive # Prepare submodules
make -C ${source_dir} jinx # Download jinx
cd <your build directory>
${source_dir}/jinx init ${source_dir} ARCH=<desired architecture, e.g. riscv64, x86_64, i686, loongarch64>
${source_dir}/jinx build limine-disk-image- Get better read-write filesystem drivers (UDF, ext4, FAT32, etc.)
- Implement working set for virtual memory (swap and page replacement)
- Implement whatever is needed to port programs
These are the features that are planned to be had in the OS:
-
Processes and threads
- Processes
- Task switching
- Preemptive multitasking (boring)
- Threads - basic pthread implementation in userspace building on kernel interfaces
- Kernel threads
- User space
-
Memory
- Page frame allocator
- Zone allocator - currently only splits memory into <4GB and >=4GB zones (for legacy devices)
- Kernel virtual memory manager
- kmalloc
- Allocating memory to userspace
- Userspace memory regions
- Mapping (anonymous mmap)
- Lazy allocations
- Full unmapping
- Partial and overlapping unmapping (munmap)
- memprotect and changing permissions
- Access to physical memory
- Mapping of memory objects
- Copy-on-write
- Memory objects
- Memory object creation from kernel
- Memory object access from kernel
- Blocking tasks on memory object access in kernel/pagers
- Memory object mapping to userspace
- Memory object creation and management from userspace
- Delayed allocation
- Memory protections
- TLB shootdowns
- Swapping
- SMP
- Page frame allocator
-
Interrupts
- Interrupt dispatching to userspace drivers
- Interrupt sharing
- Proper interrupt priorities
-
IPC and messaging
- Buffered string messages
- Ports
- Kernel messages
- Quick user memory access - catching exceptions on userspace memory access
- Rights (handles/capabilities)
- Send once/many rights
- Memory objects rights
- Interrupt rights
- Timer rights
- Phys memory/iommu rights
- I/O ports rights
- Notification of closed rights
- Sending rights over IPC
-
Permissions-> capabilities -
Multi CPU support
- 3 to 5 level page tables
- Time
- ACPI PM Timer (clock source and calibration)
- HPET (clock source and calibration)
- LAPIC timer
- TSC
- TSC deadline
- KVM clock
- APIC
- xAPIC
- x2APIC
- LAPIC
- IOMMU
- 5 level paging
- FRED
- Virtual memory
- 2 level page tables
- PAE - supports and uses up to 16GB of RAM, if available
Core utilities and daemons
- Process management (processd) - Mostly unfinished, I plan it to route signals
- Networking (networkd)
- Filesystems
- ext4 - With the help of ext4plus crate in userspace
- Partition probing
- Mounting
- Reading
- Writing - not supported by the driver
- UDF
- FAT32
- FUSE
- USTAR filesystem daemon - Archive parsing works, but it is very incomplete
- ext4 - With the help of ext4plus crate in userspace
- Drawing on screen (screend)
- Framebuffer
- Showing text with framebuffer
- Framebuffer
- Human input
- Native executables
- POSIX compatibility layer - Many functions are implemented and working, but a lot more are yet to be done
Drivers
- PS/2
- Keyboard - works, but needs something to funnel the input to
- Mouse
- i8042
- ATA/Bulk storage
- AHCI
- Controller detection and initialization
- Device detection
- DMA
- interrupts handling
- Reading
- Writing - no filesystems yet
- NVMe
- Virtio block
- AHCI
- USB
- Serial/Parallel - ns16550 using interrupts for receiving and sending. Works on RISC-V, missing IO ports stuff and untested on x86.
- PCI/PCIe
- Device detection
- Legacy interrupt routing - Works on x86, broken on RISC-V, likely easy to fix
- MSI/MSI-X
- Network cards
- virtio
- virtio-user-input
- ACPI (using uACPI)
- Initialization
- IRQ routing
- Power button (interrupt) and shutting down
- EC driver
- AMD GPIO driver
- S3 sleep - kinda works, but doesn't have a proper interface for drivers
Userland
- Screen/keyboard tty
- ns16550 serial console
- GUI
- C/POSIX Library - using mlibc
- Init server
- Launching servers
- Matching pmbus objects
Languages/Runtimes
- LLVM/Clang patch
- Hosted GCC cross-compiler
- C and its runtime (using homemade C/POSIX library)
- C++ and libstdc++ (and other) runtime(s) - Compiling and working but a lot of C library functions are missing
- Go and libgo runtime with GCC - Compiling, but untested and probably broken for now. Also, exec-stuff is missing
The following libraries have been used in the project, possibly containing their own licenses:
Unless otherwise stated (see the dependencies), the project is licensed under the BSD 3-Clause license. See the LICENSE file for more information.