Build your own OS (part 6)

Pasan Devin Jayawardene
4 min readAug 27, 2021

--

User modes

Hello and welcome back to the sixth part of my OS developing article series. Up to now, we integrated a couple of things into our OS including inputs, outputs, and segments (I will put the links to previous articles at the end in case you missed them). In this article, I will explain how we can execute a simple user program with our operating system. we will start by getting familiar with the user mode.

User Mode

Typically, a kernel is not supposed to perform application logic, but rather to assign that to applications. The kernel creates the necessary abstractions to facilitate application development, performs tasks on behalf of applications (via system calls), and schedules processes.

However, in this article we are not going to execute programs in user mode. Instead we will see how we can execute a small program in kernel mode.

Loading a Third-Party Program

What is the source of the external program that we are going to use? We need to load the code we want to execute into memory in some way. We can use GRUB’s modules feature to achieve this.

GRUB can load arbitrary files into memory from the ISO image, and these files are commonly referred to as modules.

To make GRUB load a module, edit the menu.lst (iso/boot/grub/menu.lst) file and add the following line at the end:

module /modules/program

Configuring loader.s

We now need to instruct GRUB on how to load our modules, as well as tell GRUB to align all modules on page boundaries when loading them (We will see what is page alignment in the coming article). To achieve those tasks, we need to update the loader.s file as follows:

In the register ebx, GRUB will store a pointer to a struct that describes the addresses at which the modules are loaded. Therefore we must push the ebx register onto the stack before calling kmain() function in order to make it an argument to kmain. Following code segment will do that for you:

Creating and compiling a simple program

At this stage, a program can only do a few things. Therefore, a very short program that writes a value to a register will be enough for a test program. Stopping Bochs after a while and then verifying that the register contains the correct number by inspecting the Bochs log will confirm that the program has run. Following is an example program (This program will write the value deadbeef to the eax register):

Save the above program in a file named program.s and compile that with the following command:

nasm -f bin program.s -o program

After running the above command, you should see a new binary file generated. Now create a folder iso/modules and move the generated file to that location.

Executing the program with C

Before we can proceed with the program, we must first determine where it is stored in memory. We can do this entirely from C if the contents of ebx are passed as an argument to kmain. ebx pointer points to a multiboot structure, which can be downloaded from this link.

The ebx register pointer passed to kmain can be cast to a multiboot_info_t pointer and the first module’s address is stored in the field mods_addr. We must find where the program resides in the memory and then jump into it. This can be done with C as follows:

Now run the OS with the same command as before (make run). On the screen, Bochs will indicate that grub has loaded a module.

When you run the OS, Bochs will indicate that grub has loaded a module

If you followed the above instructions correctly, you should see the value deadbeef written on the eax register after closing Bochs. This can be done by checking bochslog using cat bochslog command.

If you see the value deadbeef written on the eax register, that means you have successfully run the program

If you are having problems with the implementation, following is the link to my Github repo where I have uploaded the relevant codes:

--

--