YmeOS — Let’s Build our own OS !!!😉

Thushara Samaraweera
6 min readJul 16, 2021

#1- Booting the OS

Introduction

These blog article series explain how we can develop our own x86 operating system. Since this is a complex task we can divide the whole task into several chapters. With this week's lecture series, we are going to learn how we can set up our developing environment and how to boot the small operating system.

The tools we going to use

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

  1. Host Operating System -We will be using Ubuntu as our host operating system. It’s a good practice to install your host OS on a virtual machine like VirtualBox to test your OS.

2. Packages — Once you 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

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

We’ll 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 series of small programs, each one more powerful than the one before it. The main programs in this process include BIOS, Bootloader, and the OS. The operating system is the final and the most powerful one.

BIOS — When the computer is turned on, a little program that follows the Basic Input Output System (BIOS) standard is launched. This program is normally kept on the PC’s motherboard on a read-only memory chip. The BIOS primarily performs some early diagnostics before handing control over to the bootloader.

Bootloader — The BIOS program will hand over control of the computer to a bootloader program. The bootloader’s job is to hand control over to the OS. However, due to hardware restrictions and other limitations, the bootloader is typically split into two pieces. In that case, the first part of the bootloader will hand the control over to the second part, which finally will give control to the OS.

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. Once GRUB has completed the transition, the OS has complete control over the computer.

Now we’ll work on developing the simplest operating system feasible. The only thing this OS will do is writing OxCAFEBABE to the EAX register.

Writing and Compiling

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

Save the code below in a file named ‘loader.s’.

Assembly code to write CAFEBABE to EAX register

The only thing this OS will do is write the very specific number 0xCAFEBABE to the EAX register. We can check if the number has successfully been written to the register once we boot the OS up.

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

nasm -f elf32 loader.s

Linking

After the code has been compiled, it must be linked to create an executable file. Because addresses less than 1 megabyte (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.

Script for Linker

Make a file named ‘link.ld’ with the linker script. Using the following command, you can now link the executable:

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

You should now see a file named ‘kernel.elf’ which is the final executable.

Getting GRUB

We will be using the GRUB Legacy stage2_eltorito as our bootloader for the implementation. The binary file for the above-mentioned bootloader can be downloaded from the following link.

https://github.com/pasandevin/efficientOS/blob/setup_booting_os/stage2_eltorito

Copy the file ‘stage2_eltorito’ 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. Use the following commands 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 make a GRUB configuration file named ‘menu.lst’. This file should instruct GRUB where to find the kernel and set various options. Use the following configuration for the file:

Configuration file for GRUB

Place the ‘menu.lst’ file in the folder ‘iso/boot/grub/’. Now the ‘iso’ folder should contain the following structure:

Folder structure for the ‘iso’ folder

Then use the following command 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 our OS with Bochs

Using the os.iso ISO image, we can now boot our OS in the Bochs emulator. 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. You can refer to Boch’s official website to find out more about Bochs configuration. Save the configuration file as ‘bochsrc.txt’ and run Bochs with the following command:

bochs -f bochsrc.txt -q

Then you can see like this,

l

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. Your OS has successfully booted if you see RAX=00000000CAFEBABE or EAX=CAFEBABE in the output.

Congratulations! We just created a simple Operating System. Next week 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 very much for reading!

Hope to see you again with the next chapter. Till then, STAY SAFE!!!

Happy reading …

-Thushara Samaraweera-

--

--