Writing your own Operating System

Writing your own operating is the most tedious programming task. You have to build software from the scratch. That means no libraries, no memory management or any other high-level programming features. Before writing operating system you should know how operating systems kick start.

The process of starting the operating system kernel

Starting Process of Computer

The Main board has special program called BIOS. At the start of the computer BIOS program is loaded into the ram. Then the Instruction Pointer of the CPU is set to this BIOS program. BIOS program reads the hard drive and loads the boot loader into the ram and sets instruction pointer to ram. Then boot loader executes and it reads the kernel from hard drive and loads it into the RAM.

The boot loader doesn’t set the stack pointer. So an operating system written in C++ there should be a method to set the stack pointer and then call the main function of the C++ program. Hence the Kernel of the OS should contains two programs. One is loader written in Assembly this can set the stack pointers and load the operating system into memory. Then the operating system written in C++.

Operating System Kernel Development Steps

As the first step let’s create four files. As in the diagram it needs loader.s kernel.cpp and to link those to it needs linker.d file and to automate the process it needs a Makefile.

Kernel.cpp

The starting point of the kernel is kernelMain. We have made it extern “C” so that the compiler will not change the name of the function. So we can call it from the assembly file using the exact same name.

In the our operating system we still can’t use the C++ libraries. So there is no printf function we can use. We have to implement the printf function by ourselves. The logic behind the printf function is that the monitor will print anything that is inserted into 0xb8000 memory location. So we just have to insert the text into that memory location.

void printf(char* str)
{
static unsigned short* VideoMemory=(unsigned short*)0xb8000;

for(int i=0; str[i]!='\0';++i){
VideoMemory[i]=(VideoMemory[i] & 0xFF00)|str[i];
}

}
extern "C" void kernelMain(void* multiboot_structure, uint32_t magicnumber)
{
printf("Hello World");
while(1);
}

loader.s

This is the basic code for the loader assembly file. kernelMain is the starting point of our kernel. This code will set the stack pointer and will call the kernelMain function in our cpp file.

.section .text
.extern kernelMain
.global loader
loader:
mov $kernel_stack, %esp //setting the stack pointer
call kernelMain //calling the kernelMain
_stop: //extra precaution to keep running
cli
hlt
jmp _stop
.section .bss
kernel_stack:

The above code has several major issues.

1. When kernel_stack is filled, it is filled from right hand side to left hand side. So it will replace some important content in kernelMain. To avoid that we should keep some space in the memory in between kernelMain and kernel stack.

.space 2*1024*1024;

This is used to add some space.

2. Boot loader will not identify this file as a kernel if we don’t set a special mark to say that it is a kernel. So we have to put a magic number in the kernel file and that is 0x1badb002.

.set MAGIC, 0x1badb002
.set FLAGS, (1<<0 | 1<<1)
.set CHECKSUM, -(MAGIC + FLAGS)
.section .multiboot // setting our magic number
.long MAGIC
.long FLAGS
.long CHECKSUM
.section .text
.extern kernelMain
.extern callConstructors
.global loader
loader:
mov $kernel_stack, %esp
call callConstructors
push %eax
push %ebx
call kernelMain
_stop:
cli
hlt
jmp _stop
.section .bss
.space 2*1024*1024;
kernel_stack:

Linker file

The linker file will combine the above two files into a one.

ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386:i386)
SECTIONS
{
. = 0x0100000;

.text :
{
*(.multiboot)
*(.text*)
*(.rodata)
}

.data :
{
start_ctors = .;
KEEP(*(.init_array));
KEEP(*(SORT_BY_INIT_PRIORITY( .init_array.* )));
end_ctors = .;

*(.data)
}

.bss :
{
*(.bss)
}

/DISCARD/ :
{
*(.fini_array*)
*(.comment)
}
}

The Makefile should be as follows

You should include the Makefile to give instructions to compile the Assembly files cpp files and then to link them.

The Makefile contains instructions to create an iso file and then run it in the VirtualBox.

You can find the full source code from this link.

Run appropriate make command to execute instructions. (If you have installed VirtualBox use make run command to run os in VirtualBox)

Undergraduate of University of Moratuwa, Department of Computer Science and Engineering.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store