Develop our own operating system! #part06

Integrate user-modes

Waruni Lalendra
7 min readAug 27, 2021

Hello everyone! Welcome again for the 6th step of building our own operating system! Last week, we developed our operating system to read inputs from the keyboard. If you missed that, you can find that article in here.

Before explain about today’s topic let me remind you something that you may haven’t notice yet. So far, we have directly interact with our operating system a lot. For example to print a text on the console or get inputs form the keyboard. But try to remember the last few times that you used a computer. What did you do? Create a report with your word processor software? Design your presentation slides for your presentation software? Test your new program codes with your IDE? Think a second. 98% of times when you work with your computer, you are using a application software to fulfill your needs, right? So, how many times do you try to directly interact with your operating system?

If your answer is ‘no’, then why is that? That is because the duty of the operating system is to work as the middle layer between application software and the hardware. That means, when you using your application software the operating system works behind the scene.

The operating system act as a middle layer between user programs and hardware.

So today, we are going to develop our operating system to execute a small user program!

User Mode and Kernel Mode

Before going coding, we need to have a clear idea about these two modes.

Kernel mode is the most privileged one and used for requesting hardware support. User mode is responsible for executing the application programs.

Kernel mode and user mode are the two operating modes of a computer. It is in user mode when the computer executes application software. The computer enters kernel mode once the application software asks for hardware support. The kernel is the core of the computer system.

The computer’s operating system regularly switches between user mode and kernel mode.

Almost all of the operating system’s important functions are carried out in kernel mode. Between User Mode and Kernel Mode, there is a fundamental difference between them: user mode is the mode in which the applications are running and kernel mode is the privileged mode to which the computer enters when accessing hardware resources.

So, with this week implementation we will demonstrate how to easily execute a small program in kernel mode because executing application program in user mode we’ll need to, beside segmentation, do paging and page frame allocation which we haven’t completed yet. Therefore for this week we only go for executing program with kernel mode.

Loading an External Program

Normal process of a loading and executing an user program.

We need to know where we can obtain the external software at first. Above diagram shows how the user execution process in done in a normal scenario. But we can not follow the exact scenario because we haven’t completed developing some features of our operating system such as paging.

As you can see code must be loaded into memory in somehow. When an operating system is more feature-rich, it has drivers and file systems that allow it to load software from a CD-ROM, a hard disk, or other persistent media.

Here, you don’t want to create all of these drivers and file systems, we will use a feature in GRUB called modules to load the program.

GRUB Modules

Our bootloader GRUB can load our small program into memory from the ISO image, and these files that are in ISO image usually referred to as modules. To make GRUB load a module or in other word load our small program, edit the file iso/boot/grub/menu.lst and add the following line at the end of the file:

module /modules/program

Now create the folder name modules inside the ISO file. You can use mkdir -p iso/modulescommand for that. This folder can store our program.

Before create this application program, the code that calls kmain must be updated to pass information to kmain about where it can find the modules. We also want to tell GRUB that it should align all the modules on page boundaries when loading them.

The Multiboot Header is a data structure in the kernel image that provides information to the GRUB about how and where to load the image, and which Multiboot features the image expects.

To instruct GRUB how to load our modules, this “multiboot header” or the first bytes of the kernel must be updated as follows:

GRUB loads the address of the multiboot info structure into ebx for you (this structure contains the address of the memory map. The figure is shown in below section). Then we can call into C code to handle the actual iteration and processing of the memory map. Therefore, to make it an argument for kmain function you need want to push it on the stack before calling kmain.

Executing a Program

When we say ‘application programs or software’ the first image in our mind would be MS word, Adobe XD or something like that. But do not go that far because we are developing a basic OS and still we are in our 6th stage. Therefore our application program is very simple one which can perform a few actions.

This program will only writes a value to a register (which is 0xDEADBEEF ) We are selecting this one because then we can check the result very easily. For that, halt Bochs after a while and then check that register contains the correct number by looking in the Bochs log.

The above mention program can present as bellow.

    ; set eax to some number, to read from the log afterwards
mov eax, 0xDEADBEEF

; enter infinite loop, nothing more to do
; $ means "beginning of line", ie. the same instruction
jmp $

We need to compile the code into a flat binary. NASM can do this with the flag -f: Open your terminal inside the module folder and run this command:

nasm -f bin program.s -o program

Finding the Program in Memory

We must locate the program’s memory location before we run it. As I menioned before assuming that the contents of ebx (which describes at which addresses the modules are loaded.) is passed as an argument to kmain function, we can do this using C language.

The pointer that describes the addresses of the modules which is in ebx register, points to a multiboot structure . The figure is shown below.

The format of the Multiboot information structure.

The pointer passed to kmain function can be cast to a multiboot_info_t pointer. The address of the first module is in the field mods_addr(Offset 24). The following code shows an example:

int kmain(/* additional arguments */ unsigned int ebx)
{
multiboot_info_t *mbinfo = (multiboot_info_t *) ebx;
unsigned int address_of_module = mbinfo->mods_addr;
}

There is way that we can check if the module got loaded correctly by GRUB or not. This can be done by checking the flags field of the multiboot_info_t structure. You should also check the field mods_count (Offset 20)to make sure it is exactly ‘1’ or not.

Jumping to the Code

Now we have already found where is our program locates inside the memory. The GRUB will load the code as we know. Then we have to jump to the code loaded by GRUB. As I mentioned before using C in more convenient that assembly we are going to use the C language in here. The code could look like this:

    typedef void (*call_module_t)(void);

call_module_t start_program = (call_module_t) address_of_module;
start_program();
/* we'll never get here, unless the module code returns */

Finally, Let’s check the result! Start the kernel, wait until it has run and entered the infinite loop in the program, and then halt Bochs, we should see 0xDEADBEEF in the register eax inside the Bochs log file. That means our operating system successfully executing our simple application program!

As a final word, Though the normal process of executing a application program is in the user mode in here, we have done everything using kernel mode. So in here we have just got an introduction to run user programs. In later sessions we will follow the exact procedure to run an application program.

In this stage our simple application program completely handled by the kernel mode.

Thank you so much for reading! until next week be motivated and keep learning! :)

Written by,

R.A.W. Lalendra

Undergraduate in Bsc(hons) Software Engineering

University of Kelaniya Sri Lanka.

--

--

Waruni Lalendra

Software Engineering undergraduate at University of Kelaniya Sri Lanka