Implement with C

Making a Small OS

Umayanga Vidunuwan
3 min readJul 23, 2021

Assembly is ideal for dealing with the CPU because it gives you complete control over every element of the program. But C is a much more convenient language to use. As a result, we’d want to utilize C as much as feasible and assembly code only when necessary.

Setting Up a Stack

We could point esp to a random area in memory since, so far, the only thing in the memory is GRUB, BIOS, the OS kernel and some memory-mapped I/O. This is not a good idea - we don’t know how much memory is available or if the area esp would point to is used by something else. A better idea is to reserve a piece of uninitialized memory in the bss section in the ELF file of the kernel. It is better to use the bss section instead of the data section to reduce the size of the OS executable. Since GRUB understands ELF, GRUB will allocate any memory reserved in the bss section when loading the OS.

We need to update loader.s file with following coding

KERNEL_STACK_SIZE equ 4096                  ; size of stack in bytes

section .bss
align 4 ; align at 4 bytes
kernel_stack: ; label points to beginning of memory
resb KERNEL_STACK_SIZE ; reserve stack for the kernel

The stack pointer is then set up by pointing esp to the end of the kernel_stack memory:

mov esp, kernel_stack + KERNEL_STACK_SIZE   ; point esp to the start of the
; stack (end of memory area)

Final view of Loader.s file is like this

Calling C Code From Assembly

The cdecl calling convention states that arguments to a function should be passed via the stack (on x86). The arguments of the function should be pushed on the stack in a right-to-left order, that is, you push the rightmost argument first. The return value of the function is placed in the eax register

For this process we need to make kmain.c file include with following C coding

int sum_of_three(int arg1, int arg2, int arg3)
{
return arg1 + arg2 + arg3;
}

Packing Structs

When using the struct in the previous example there is no guarantee that the size of the struct will be exactly 32 bits - the compiler can add some padding between elements for various reasons, for example to speed up element access or due to requirements set by the hardware and/or compiler. When using a struct to represent configuration bytes, it is very important that the compiler does not add any padding, because the struct will eventually be treated as a 32 bit unsigned integer by the hardware. The attribute packed can be used to force GCC to not add any padding

Build Tools

We need to create Makefile include following coding

We should now be able to start the OS with the simple command make run.

Now we can see compile the kernel and boot it up in Bochs

--

--

Umayanga Vidunuwan

| Software Engineering Undergraduate| University Of Kelaniya | Sri Lankan|