monOS is a 64-bit monolithic hobby OS written in Rust, inspired by the vtuber Mono Monet of V4Mirai (and TempleOS, sorta).
this project is still very much in active development. dont expect anything usable (or working) at all. i would consider this usable once most of the points on the big todo list are done. if you want to try out the os regardless, check here for some more info
- make a toy operating system thats interesting enough to play around with for a while
- explore ways of making building applications as easy and accessible as possible
- learn a bunch about computers while working on it!
- write a seriously usable os
- tools
- terminal
- editor (wip)
- paint (wip)
- task manager (wip)
- calculator (wip)
- games
- doom
- cibo online! (wip)
- other
- rooftop (desktop environment)
while monOS may not be written in its own programming language, it comes with one! monoscript is monOS's very own scripting language, designed to be as simple as possible. monOS's terminal uses monoscript for its commands and more importantly its tightly integrated with monodoc (see below), allowing you to easily write applications for monOS with minimal programming knowledge
accompanying monoscript, there is monodoc. monodoc is inspired by Markdown, HTML and TempleOS's DolDoc. in its simplest form, monodoc lets you write formatted text documents with headings, tables, links between documents etc. it can do much more than that though. you can modify the layout of text and even embed monoscript inside it to make monodoc documents interactive. this means you can write full tools and games directly from inside monOS. in fact, the following applications shipped with monOS are written entirely using monodoc/monoscript:
- task manager
- calculator
making a hobby OS has been something i've wanted to do for quite a while, but never quite got around to. when i watched mono's TempleOS stream, it sort of brought the idea back into my head - together with a quite obvious theme :P
for the same reason, monOS has(/will have) quite a lot of references to TempleOS.
because i like torturing myself (and for educational reasons) the end goal for the kernel is to use no external dependencies apart from the bootloader. i handle this by first getting a basic implementation running using existing crates and then gradually substituting it with my own implementations. this makes things a lot less painful than writing everything from scratch directly.
monOS is bootable from both BIOS and UEFI. that being said, most of my own testing was done on UEFI. therefore i recomend always using the UEFI image if you can!
BIOS boot is currently utterly broken and will not be supported going forward, sorry. maybe sometime later...
some of these have additional documentation in their respective readmes so i highly recommend looking around if youre interested!
monos_kernel
contains the kernel code (aka the main thing)monos_std
is the library that userspace programs link against.monos_gfx
is the ui library used by graphical applications- the
userspace
directory contains the included userspace programs that get shipped together with the kernel. these currently include:hello_world
basic template applicationrooftop
monOS's desktop environment. also provides a library for window clients!terminal
monOS's terminal. commands are single line monoscript instructionsdoom
would it really be a complete operating system without being able to run doom?
- the
os_disk
directory will be used to construct the ramdisk (see below) monoscript
is a (platform independent) library containing the monoscript parser and runtimemonoscript_emu
contains a minimal monoscript runner for windows/linux/macOS
only PS2 mouse/keyboard support for now. i want to implement USB at some point but it seems like a huge pain. most vms and even hardware emulates PS2 from USB devices anyways. monOS does use the newer APIC instead of the old fashioned 8259 PIC since its support seems to be (basically?) nonexistent under UEFI.
the kernel is mapped into the higher half of memory (at 0xffff800000000000
) and currently has a stack size of 1MiB and a heap size of 16MiB.
type | start | size | page table indices |
---|---|---|---|
userspace code | 0x200_000 | ( 0, 0, 1, 0, 0) | |
userspace heap | 0x28_000_000_000 | ( 5, 0, 0, 0, 0) | |
userspace stack | 0x400_000_000_000 | 4 MiB | (128, 0, 0, 0, 0) |
mapped framebuffer | 0x410_000_000_000 | (130, 0, 0, 0, 0) | |
------------------ | ---------------------- | ------ | ------------------ |
kernel code | 0xffff_800_000_000_000 | (256, 0, 0, 0, 0) | |
kernel framebuffer | 0xffff_900_000_000_000 | (288, 0, 0, 0, 0) | |
kernel heap | 0xffff_a00_000_000_000 | 16 MiB | (320, 0, 0, 0, 0) |
control structures | 0xffff_b00_000_000_000 | (352, 0, 0, 0, 0) |
monOS currently uses the following algorithms for allocating memory:
type of memory | allocation algorithm |
---|---|
pages | bitmap allocator |
heap memory | linked list |
userspace heap | linked list |
control structures | bump allocator |
these were basically all just chosen because they were the easiest to implement. memory performance really isn't a big concern for this OS.
the heap allocator is using the linked_list_allocator crate right now, because i couldn't be bothered to write my own. its something i still want to do at some point though.
the control structures refer to various mappings needed by the kernel internally, for example mapping acpi tables into memory.
monOS manages these mappings automatically using a Mapping
struct that unmaps them once they go out of scope. the virtual address of these mappings is then just allocated using a simple bump allocator.
monOS (currently) has no way of accessing external disks, all data is kept within a ramdisk. this means that everything gets wiped when the os reboots. feel free to wreak havoc :P. the ramdisk itself is a FAT16 image with the following structure:
/bin
: userspace programs/home
: user directory/data
: OS resources. you probably shouldn't touch these unless you want to break something >:D
the build script automatically builds all the crates in the userspace
directory and puts their elf binaries in /bin
monOS's vfs model closely follows the one linux uses (albeit much simplified). the vfs is represented as a tree structure that gets lazily loaded into memory as needed. vfs nodes currently are either files or directories, i dont think ill add symlinks since they would complicate links a lot further. one filesystem gets used as the root and then other filesystem implementations can be mounted inside empty directories (this is currently unused though...).
i have thought about replacing the current messaging functionality with virtual files aswell since it would simplify the usage from userspace. the kernel implementation would be relatively complex though and also probably not as fast so itll stay as a idea for now.
the following syscalls currently exist:
id | name | param 1 | param 2 | param 3 | param 4 | description |
---|---|---|---|---|---|---|
0 | spawn | file path ptr | file path len | spawn a new process of the given binary | ||
1 | yield | suspend the current process and move it to the end of the process queue | ||||
2 | serve | port name ptr | port name len | provide a channel on the given port | ||
3 | connect | port name ptr | port name len | ptr to Option<ChannelHandle> |
connect to a channel at the given port | |
4 | wait_conn | port name ptr | port name len | ptr to Option<ChannelHandle> |
wait for a process to connect to a channel on the given port | |
5* | send | data 1 | data 2 | data 3 | data 4 | send data over a opened channel (asynchronously) |
6* | receive | ptr to Option<Message> |
block until data is received on a given opened channel | |||
7 | receive_any | ptr to Option<Message> |
block until data is received on any opened channel | |||
8 | req_chunk | requested size | request a memory chunk of the given size. returns address of chunk or 0 on failure | |||
9 | open | file path ptr | file path len | ptr to Option<FileHandle> |
open a file at the given path | |
10 | close | FileHandle |
close a opened file | |||
11 | seek | FileHandle |
offset | seek to a specific position in a opened file | ||
12 | read | FileHandle |
buffer ptr | buffer len | read len bytes from a opened file | |
13 | write | FileHandle |
buffer ptr | buffer len | write len bytes to a opened file | |
14 | list | file path ptr | file path len | ptr to [ArrayPath; n] |
arr len | list directory entries. reads only param 4 amt, you should stat the dir first |
15 | string ptr | string len | print a string somewhere (serial port currently). should only be used for debugging. | |||
16 | sys_info | SysInfo |
returns a single u64 describing the state of the requested SysInfo |
*the syscall id is a bit special for the send
, and receive
syscalls (see the chapter on messaging below).
inter-process communication in monOS happens over channels. a process can provide a channel on a port (basically just a unique string) using the serve
sycall.
other processes can then open a connection on the port using the connect
syscall. this provides both the sending and the receiving process (using the wait_conn
syscall) with a channel handle.
both processes can then send and receive messages over the channel using the send
, receive
and receive_any
syscalls.
a message is either scalar consisting of 4 64-bit values or a chunked message. a chunked message points to a previously requested (using the req_chunk
syscall) memory chunk
that will then be unmapped (unless otherwise requested) from the address space of the sending process and mapped into the address space of the receiving process.
some messaging related syscalls are a bit special since they use the syscall id to pass some additional parameters:
bits | content |
---|---|
0- 7 | syscall id |
8 | 0 = scalar values, 1 = memory chunk |
9 | for chunk: 1 = memory mapped |
10-15 | nothing... for now :) |
16-63 | channel handle |
a channel handle is a 48-bit value consisting of 32 bits target process id, 8 bit target channel id and 8 bit sender channel id.
there is currently no safety in place for channels, meaning that a process can just send to any channel knowing its channel handle without connect
ing to it first. i should probably fix that at some point...
- it boots!
- working framebuffer
- text drawing
- double buffering
- exception handling
- basic handlers
- better panic handling
- handle panics when the framebuffer isn't initialized yet
- show a extra screen on panic
- GDT/TSS for seperate exception stacks
- memory
- paging
- address translation
- page mapping/unmapping
- frame allocation
- use a better allocator (maybe)
- heap allocation
- basic implementation
- implement own allocator (maybe)
- fix heap corruption
- paging
- ACPI
- basic table parsing
- hpet timer
- APIC
- local apic
- timer interrupts
- calibrate timer using HPET
- timer interrupts
- io apic
- ps2 keyboard input
- ps2 mouse input
- local apic
- gui
- image loading
- .ppm loading
- turn fb into image for fast screen clearing
- decently usable immediate mode gui library
- basic functionality
- better text wrapping
- buttons
- text input
- scrollable
- "drawing" functionality
- only rerender if necessary
- rewrite fb drawing code because it is messy as hell
- boot screen
- kernel panic screen
- desktop environment (
rooftop
)- basic ui
- only rerender necessary parts
- spawning windows
- usable client
- keyboard input
- moving windows
- auto refresh desktop entries
- image loading
- task management
- async executor
- process spawning
- scheduler
- process killing
- getting to userspace
- map kernel to upper half
- it works!
- elf file loading
- using own implementation
- syscalls
- it works!
- framebuffer access
- spawning/killing processes
- spawning
- killing
- filesystem access
- system timer (using hpet)
- userspace memory
- heap
- sensible memory structure
- ondemand paging
- memory chunks
- free on process exit
- ipc
- kernel <-> process
- sending memory chunks
- block waiting processes
- process <-> process
- mpsc/broadcasts (?)
- save sse/avx context
- terminal
- basic functionality
- fix overflow colors
- more inbuilt functions
- running doom!! :)
- figure out linking
- scuffed libc port
- it works!
- keyboard input
- mouse input
- monoscript
- basic parsing
- missing statements
- function declarations
- function return values
- if/else
- while
- sound hook
- running in the emulator
- running in the real thing
- repl (terminal)
- editor
- docs
- improve execution performance (?)
- monodoc
- parsing
- viewing
- editing
- docs
- filesystem support
- ramdisk reading
- fat16 drivers
- multi-lfn file names
- decent vfs implementation
- keep track of opened files to avoid conflicts
- block device drivers/persistent data
- networking
- get access to the network card/pcie devices at all
- get smoltcp (or some other TCP stack) running
- write a simple http implementation (or use an existing one, idk)
- get websockets working
- Cibo Online! client
- multiprocessor support (maybe)
- USB support (maybe)
- whatever else comes to mind (suggest something!) :)
you'll need a working rust installation. install llvm-tools (rustup component add llvm-tools-preview
) and then do a cargo build
in the workspace root. the disk image will be in target/debug/build/monos-xxxxxxxxxxx/out/uefi.img
. if you have qemu installed, you can just do cargo run
and it will be started automatically after the build :)
running in virtualbox seems to work great too. convert the image into a virtualbox .vdi
disk image using qemu-img (qemu-img convert uefi.img -f raw -O vdi uefi.vdi
) or a similar tool. set the vm type to "Other" and the version to "Other/Unknown (64-bit)". make sure to tick the "Enable EFI (special OSes only)" option at the hardware step or it won't work.
compiling the doom executable also requires the x86_64-elf-gcc
compiler to be installed on your system which may cause the build to fail. install it or skip the doom build by temporarily (re)moving the userspace/doom
directory!
- to mono for being cool and based and providing me with lots of entertainment while i wrote this thing :3
- to my friends for putting up with me constantly bothering them about this project <3
- to Philipp Oppermann's amazing blog series for getting me started on this at all
- to this blog post for getting me on the right track with getting into userspace
- to all the developers of the x86_64 crate that served as a great reference point for my own implementations
- to EuraliOS, moros, Hermit OS and Redox OS for also serving as awesome references
- to the OSDev Wiki
please contact me if you want your work removed. i wasn't able to find decent licensing information for all of these and am using them under the assumption that using for non-profit fan content is fine.
- woofycakes for the mono emotes in this file
- slavfox for Cozette, the system font
- benwr for Glean, the (smaller) system font and Jim Knoble for Neep, the font that Glean is originally based on