Build your own operating System-part02

Pasan lahiru dissanayake
4 min readJul 22, 2021

Implement_with_c

Last week ,we guided you to setup the booting part of our operating system. In this article, we are going to implement C language to our project instead of Assembly language.

So, let’s get started!

Setting Up a Stack

Since all non-trivial(not lightweight) C programs use a stack, and setting up a stack is not harder than to make the esp register point to the end of an area of free memory. So far, in this development process, the only things in memory are GRUB, BIOS, the OS kernel, and some memory mapped I/Os. This is not a good thing to do; because, we don’t know how much memory is available or if the esp pointed memory area is used by something else.

Reserving a piece of uninitialized memory in the bss section in the ELF file of the kernel will be a solution. And also, this will reduce the OS executable size.Add this section to loader.s file.

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

And then, we need to setup the stack pointer by pointing esp to the end of the kernel_stack memory. In order to do that, you need to add the following statement inside the loader: block you your loader.s file

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

If you want to know how loader.s file look like ,you can follow this link:

skyOS/loader.s at c8fc368b90a4cd83c30705ded52645836effb9bf · pasanlk/skyOS (github.com)

Calling C Code From Assembly

The next step is to call a C function from assembly code. There are many different conventions for how to call C code from assembly code . This book uses the cdecl calling convention, since that is the one used by GCC. 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. The following code shows an example:

/* The C function */
int sum_of_three(int arg1, int arg2, int arg3)
{
return arg1 + arg2 + arg3;
}

We need to call this c function using assembly.So place this following code after the esp instruction in the loader label of the loader.s file.

; The assembly code
external sum_of_three ; the function sum_of_three is defined elsewhere

push dword 3 ; arg3
push dword 2 ; arg2
push dword 1 ; arg1
call sum_of_three ; call the function, the result will be in eax

Compiling C Code

The next step will be this. For normal compilations, we can use gcc fileName.c -o objectName. But, in this case, we are compiling them for an operating system. So, we have to use a lot of flags as below.

-m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs

And, we recommend you to turn on all warnings and treat warnings as errors by adding these flaags:

-Wall -Wextra -Werror

Build Tools

Now we’ll set up some build tools to make compiling and testing our operating system easier. We will use make as our build system.

Here’s a simple Makefile for our OS:

OBJECTS = loader.o kmain.o
CC = gcc
CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \
-nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c
LDFLAGS = -T link.ld -melf_i386
AS = nasm
ASFLAGS = -f elf

all: kernel.elf

kernel.elf: $(OBJECTS)
ld $(LDFLAGS) $(OBJECTS) -o kernel.elf

os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
genisoimage -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-boot-load-size 4 \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso

run: os.iso
bochs -f bochsrc.txt -q

%.o: %.c
$(CC) $(CFLAGS) $< -o $@

%.o: %.s
$(AS) $(ASFLAGS) $< -o $@

clean:
rm -rf *.o kernel.elf os.iso

The contents of our working directory should now look like the following figure:

.
|-- bochsrc.txt
|-- iso
| |-- boot
| |-- grub
| |-- menu.lst
| |-- stage2_eltorito
|-- kmain.c
|-- loader.s
|-- Makefile

You should now be able to start the OS with the simple command make run, which will compile the kernel and boot it up in Bochs.

Reference: https://littleosbook.github.io/

Following is the Github repository for this: from:https://github.com/pasanlk/skyOS.git

THANK YOU!

--

--