This OS+Bootloader was used to strengthen my knowledge of Rust, x86, and Operating Systems
The bootloader is written fully in assembly and the kernel in Rust
$ make
to build
$ make run
to run in qemu
$ make debug
to wait for gdb to attach
$ make clean
to clean build artifacts
Rust (nightly)
nasm
qemu-system-x86_64
python3
The bootloader will get the processor running in 64 bit mode and identity map all of memory
Once the kernel is loaded it creates a frame allocator using the boot info from the bootloader.
To do this it will compare the good memory regions with pages that overlap. It skips the region starting at 0x0 and all regions that are not type 0x1. It will create a singly linked list where the first u64 of the page points to the phys addr of the next page. The last page will point to 0xdeadbeef just as a safety check.o
Using the frame allocator the heap is created, initially with as much contiguous pages as there is, and then extended. This is done because I keep track of the contiguous regions of the heap, the first time I don't know how many contiguous memory regions there will be so I just limit this to one. Once the heap is initialized I can now return heap regions in a vector for when I don't know how many regions will be needed for some heap size.
I then create a new page table with a recursive index, map in the heap at a higher and contiguous address, the stack, map the elf at the current mapping, and at some new mapping (high address), I also identity map the vga buffer.
I then translate anything which has a heap address to the new mapping
I then jump to the second phase which will be using the new page table.
The phase 2 function will never return. Also to help with the transition I have a helper function to convert the arguments back to proper safe types and then call into the actual phase 2 function.
On entry of phase 2 the stack and heap should be setup with at high addresses and with contiguous virtual pages, and the kernel mapped into a high address with the frame allocator containing all of the unused frames.
interrupts and exceptions
file system processes and threads
IPC
Video
IO
Networking
Trying to figure out how to get an executable that is 100% Position independent, currently there is still GOT, Vtables and string references that have absolute addresses. I could translate these, but it would be cleaner if I did not need to, and I think it is possible: rust-lang/rust#87934 (comment)
https://github.com/rust-osdev/bootloader
https://github.com/gamozolabs/chocolate_milk
probably others I am missing