LonelyOS title image

Why

Why would you ever try to create your own OS? I can only speak for myself:
- Because I can
- To get a deeper understanding of the way computers work
- To become a better engineer

Because it’s a cool feeling when every line of code running on a machine is yours.

OSDev

I heart OSDev There’s absolutely no chance I could have done anything I’m about to describe without the amazing OSDev community! If you’re ever interested in creating your own OS, the Wiki and Forum are a great place to start.

What Is An OS?

Kernel space vs user space Usually you write programms running in the user space of an OS. The OS provides you with nice things like memory management, scheduling, protection, user interface, etc.
The kernel provides abstractions to things like files, sockets, processes, etc. You can use these abstractions to write your code. My goal is to write a “simple” kernel and OS that allows you to write user space programs.

X-compiling

λ ~/ gcc -dumpmachine
arm64-apple-darwin21.3.0

I’m writing my OS on Apple Silicon hardware. But the target architecture for my OS is Intel x86_64. And I’m not writing code for apple-darwin, I’m writing code for LonelyOS. So, I can’t just use the normal gcc that I install with brew to compile my OS. Therefore I needed to compile my own GNU C Compiler to target the correct system. There’s a great tutorial on OSDev on how to compile your (X-)compiler.

Multiboot

The bootloader is part of the things that run when you power on your computer. It’s job is to load the kernel into memory and pass control overt to iy. It’s possible to roll your own bootloader. I decided against this option and instead just used GRUB. GRUB complies with the Multiboot specification. All I need to do is setting some magic numbers to “show GRUB my kernel is complying with the standard:

# Declare constants for the multiboot header.
.set ALIGN,    1<<0             # align loaded modules on page boundaries
.set MEMINFO,  1<<1             # provide memory map
.set FLAGS,    ALIGN | MEMINFO  # this is the Multiboot 'flag' field
.set MAGIC,    0x1BADB002       # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot

# Declare a header as in the Multiboot Standard.
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

Then place the multiboot section at the correct place in memory with help of the linker:

    /* First put the multiboot header, as it is required to be put very early
	   early in the image or the bootloader won't recognize the file format.
	   Next we'll put the .text section. */
	.text BLOCK(4K) : ALIGN(4K)
	{
		*(.multiboot)
		*(.text)
        ...
    }

Finally initialize the stack (ESP) and call the main method of the kernel:

call init
call kernel_main

Hello, World!

Hello, world! in QEMU Finally. Control was passed to kernel_main. I no longer need to deal with placing magic numbers at the correct place in memory with assembly madness. It’s time to write “Hello, world!” to my screen.
But wait… There is no printf method.
The printf method is the first “feature” I added to LonelyOS. To not make this post explode I’m not going into more detail here, but to see how it was done, have a look at the code

What’s Next?

This project will not be “done” in the next few months. Probably it will never be “done” at all. Next I’ll start handling hardware interrupts, so I can type things on my keyboard. I’ll add a minimal shell to run commands and provide a glibc for user space programs to use! I hope I can post an update on the progress here before the year is over!

so long

comments powered by Disqus