DylanOS — Developing a Small Operating System!! 😜

Vidhush Thamilchelvan
6 min readOct 1, 2022

--

Developing an Operating System (OS) is not an easy task, and the question “How do I even begin to solve this problem?” is likely to come up several times during the course of the project for different problems. In these blog article, we will explain how to write a small Operating System which displays the Hardware Information of the computer. Since this is a complex task, we can divide the whole task into several chapters.

#1 — Setting up the Booting part of OS

With this chapter, we are going to learn to set up our developing environment and boot the small operating system.

Prerequisites

First of all, let’s decide what tools and technologies we going to use in our implementation so that we can get started.

Host Operating System — We need a host operating system (like Ubuntu) for doing OS development, running it both physically and virtually. It’s a good practice to install your host OS on a virtual machine like VirtualBox to test your OS virtually. In this article, we use Ubuntu 20.04 installed in a virtual machine as the host OS.

Ubuntu 20.04 as Host OS running on VirtualBox

Packages — Once we have Ubuntu OS up and running, the following packages should be installed using the terminal. (Use the command below)

sudo apt-get install build-essential nasm genisoimage bochs bochs-sdl

Programming Languages — We will use the C Programming language with GCC compiler to develop our OS.

We will be using NASM as the assembler for writing assembly code. Bash will be used as the scripting language.

Booting Process

In order to create an operating system, it is essential to understand how it boots up. This chapter will guide you through the OS booting process.

The process of booting an operating system involves transferring control through a chain of small programs, each one is more powerful than the previous one. The main programs in this process include BIOS, Bootloader, and the OS. The operating system is the final and the most powerful one.

When the computer is turned on, BIOS (Basic Input Output System) program stored in the ROM will be launched. The BIOS primarily performs some early diagnostics before handing control over to the bootloader.

The BIOS program will hand over control of the computer to a Bootloader GRUB. The bootloader’s job is to hand control over to the OS. However, due to hardware restrictions and other limitations, the bootloader is typically divided into two parts called GRUB1 and GRUB2. In that case, GRUB1 will hand the control over to GRUB2, which finally will give control to the OS.

By jumping to a memory location, GRUB will hand over control to the Operating System. GRUB will seek a magic number before jumping to guarantee that it is jumping to an OS and not some arbitrary code. The multiboot specification, with which GRUB complies, includes this magic number. After the transition, the OS has complete control over the computer.

Writing and Compiling the OS

Now we will work on developing the simplest Operating System feasible. The only thing this OS will do is writing OxCAFEBABE to the EAX register.

This section of the OS must be developed in assembly language. C programming language cannot be used since it requires a stack, which isn’t available.

The code below in a file should be saved as ‘loader.s’.

According to mentioned previously, this OS will write a specific number 0xCAFEBABE to the EAX register. Once the OS is boot up, we can check if the number has successfully been written to the register.

Next, the following command should be used to compile loader.s into a 32-bit ELF object file.

nasm -f elf32 loader.s

Linking the Kernel

After the code has been compiled, it must be linked to create an executable file. Because addresses less than 1 MB are used by GRUB, BIOS, and Memory mapped I/O, we want GRUB to load the kernel at a memory address greater than or equal to 0x00100000 (1 MB). We can use the following script as the linker.

There should be made a file named ‘link.ld’ with the linker script. Using the following command, we can now link the executable:

ld -T link.ld -melf_i386 loader.o -o kernel.elf

We can now see a file named ‘kernel.elf’ which is the final executable.

Getting GRUB

We will be using the GRUB Legacy stage2_eltorito built by GRUB 0.97 as our bootloader for the implementation. The binary file for the above-mentioned bootloader can be downloaded from given Github link:

DylanOS_simple-OS/stage2_eltorito at main · Vidhush-920/DylanOS_simple-OS (github.com)

The file ‘stage2_eltorito’ should be copied to your working directory.

Building the ISO image

The executable must be stored on a medium that a virtual or physical machine can read. For that, we will be using ISO image files in our project. We can use the program genisoimage to create the image.

A folder that contains the files that will be on the ISO image should be created. The following commands are used to create the folder and copy the files to their proper locations:

mkdir -p iso/boot/grub              # create the folder structure 
cp stage2_eltorito iso/boot/grub/ # copy the bootloader
cp kernel.elf iso/boot/ # copy the kernel

Then a GRUB configuration file named ‘menu.lst’ should be made. This file should instruct GRUB where to find the kernel and set various options. Here is the following configuration for the file:

The ‘menu.lst’ file will be placed in the folder ‘iso/boot/grub/’. Now the ‘iso’ folder should contain the following structure:

iso
|--boot
|--grub
||--menu.lst
||--stage2_eltorito
|--kernel.elf

Then the following command will be used to create the iso file.

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

A file named ‘os.iso’ will be generated. This contains the kernel executable, the GRUB bootloader, and the configuration file.

Running Bochs Emulator

As the final step, we can now boot our OS in the Bochs emulator using the ‘os.iso’ ISO image. To get started with Bochs, you’ll need a configuration file. Following is a simple configuration file.

Depending on how you installed Bochs, you may need to adjust the path to romimage and vgaromimage. We can browse Boch’s official website as a reference to find out more about Bochs configuration. The configuration file should be saved as ‘bochsrc.txt’ and there should be run Bochs with the following command:

bochs -f bochsrc.txt -q

Then you can see like this,

Give continue on Terminal

After that, you can see,

Bochs should now be running and presenting a console with some information on it. Quit Bochs and display the log generated by Bochs with the command below:

cat bochslog.txt

The contents of the registers of the CPU replicated by Bochs should now appear somewhere in the output. The OS has successfully booted if we can see RAX=00000000CAFEBABE or EAX=CAFEBABE in the output.

Congratulations! We just created a simple Operating System. In Next chapter, we’ll see how we can get the help of C Programming Language to further develop our OS.

References:

The Little OS Book: https://littleosbook.github.io/book.pdf

Further Readings:

1. http://duartes.org/gustavo/blog/post/how-computers-boot-up

2. http://duartes.org/gustavo/blog/post/kernel-boot-process

3. http://wiki.osdev.org/Boot_Sequence

Thank you for reading!

Hope to see you again with the next chapter.

-Vidhush Thamilchelvan-

--

--

Vidhush Thamilchelvan
Vidhush Thamilchelvan

No responses yet