Build your own operating system #6_integrate_user_modes
In previous article, we discussed about how we can handle interrupts and get inputs with our operating system. Hope you could do it successfully. In this article, we are going to learn about how we can create a user mode to execute user programs, in contrast with kernel mode.
Let’s go.
User modes
There are two modes of operation in the operating systems. These are user mode and kernel mode.
The system is in user mode when the operating system is running a user application such as handling a text editor. The transition from user mode to kernel mode occurs when the application requests the help of operating system or an interrupt or a system call occurs.
This environment is less privileged than the kernel, and will prevent (badly written) user programs from messing with other programs or the kernel. Badly written kernels are free to mess up what they want.
Loading an External Program
Usually, operating systems have drivers and file systems that enable them to load the software from a CD-ROM drive, a hard disk or other persistent media.
For that, we will be using a feature in GRUB, called modules instead of creating all these drivers and file systems.
GRUB modules
GRUB can load any arbitrary into the memory from the ISO
image. Those files are usually referred to as modules.
In order to make GRUB loads a module, you have to add the following line to the end of the iso/boot/grub/menu.lst
file. Which will use the program
file as a module.
module /modules/program
Now, we need to create the /modules
directory in the /iso
directory. Use the following command:
mkdir -p iso/modules
Creating a program
Then we have to create a very simple program and compile it to make a binary file from that. Copy the following code and save it in a file called program.s
in the /modules
directory.
Compiling the program
In order to run the above created program, we need to compile it first. Since our kernel cannot parse advanced executable formats we need to compile the code into a flat binary. NASM can do this with the flag -f
:
nasm -f bin program.s -o program
IMPORTANT: Make sure to execute this command in a new terminal window from in the /modules
directory. Or you can replace the correct path.
Instruct the GRUB
To instruct GRUB how to load our modules, the “multiboot header” — the first bytes of the kernel — must be updated as follows:
GRUB will also store a pointer to a struct
in the register ebx
that, among other things, describes at which addresses the modules are loaded. Therefore, you probably want to push ebx
on the stack before calling kmain
to make it an argument for kmain
.
;loader.s calling the kmainpush ebx ;push the ebx register as an argument to kmain
call kmain ;call the kmain function
Finding the Program in Memory
Before moving forward, we must find where it locates in the memory. We can accomplish this task totally in C if the content of ebx
register is supplied as an argument to kmain
function.
The pointer in ebx
points to a multiboot structure. Download the multiboot.h
file from http://www.gnu.org/software/grub/manual/multiboot/html_node/multiboot.h.html, which describes the structure.
After downloading the file, include the file to kmain.c
.
The pointer, which passed to kmain
as an argument(ebx
register) can be cast to a multiboot_info_t
pointer. The address of the first module is in the field mods_addr
. See the following code:
Now, we need to make sure that our module has loaded correctly by the GRUB. This can be done by checking the flags
field of the multiboot_info_t
structure. You should also check the field mods_count
to make sure it is exactly 1.
Jumping to the code
The only thing left to do is to jump to the code loaded by GRUB. Since it is easier to parse the multiboot structure in C than assembly code, calling the code from C is more convenient (it can of course be done with jmp
or call
in assembly code as well). The C code could look like this:
Here is my kmain.c
file, if you are not clear about the above discussed things:
If you have completed all the above steps, you should be able to run the OS in Bochs
. Now, 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
via the Bochs
log.
You can download a completed code that I have created for integrating user modes for the OS from: here
I hope you have successfully integrated user modes to your operating system. Catch you in the next article.
Thank you!