Implementing a simple Operating System (01-Booting the OS)

Lahiru Chathuranga
6 min readJul 15, 2021

Operating system

The operating system provides a interface between user and the physical computer’s hardware. The users interact with the user Applications like Microsoft word , excel and so on. This softwares don’t know how to deal with the computer’s hardwares. And also the users don’t know about this. User Applications can’t run without having a operating system in the computer. And user Applications run on the operating system. So that computer must has at least one operating system. As of today we can run a virtual machine in the computer and then we can use (install) another operating systems in this virtual machine.

What does operating system do?

  1. Manage the computer’s resources, such as the central processing unit, memory, disk drives, and so on.
  2. Establish a user interface.
  3. Execute and provide services for applications software.

Implementing a operating system is not a easy thing. We have to consider many things to do that.

First we want to install a virtual machine to our computer.

Virtual Machine

A virtual machine is the emulation of a computer system. Virtual machines are based on computer architectures and provide functionality of a physical computer. Their implementations may involve specialized hardware, software, or a combination.

Why we should use a virtual machine

If we going to develop an OS, it will be very easy to do that with virtual machine rather than do that with our physical computer. Since starting our OS in a virtual machine is much faster than getting our OS onto a physical medium and then running it on a physical machine.

I used Oracle VM as my virtual machine, and also you can go to download page of the Oracle with this link.

And after we want to set up an OS to this virtual machine. We can use Linux, Ubuntu and like these Linux base operating systems.

I used Ubuntu as the OS. You can use this link to download Ubuntu.

Once Ubuntu is installed, either physical or virtual, the following packages should be installed using ,

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

We know, developing an operating system requires a very precise control of the generated code and direct access to memory. So we must use programming language like c to implement an OS.

Booting an operating system consists of transferring control along a chain of small programs, each one more “powerful” than the previous one, where the operating system is the last “program”.

As your PC’s most important startup program, BIOS, or Basic Input/Output System, is the built-in core processor software responsible for booting up your system. Typically embedded into your computer as a motherboard chip, the BIOS functions as a catalyst for PC functionality action.

The BIOS program will transfer control of the PC to a program called a bootloader. The bootloader’s task is to transfer control to the OS.

A lot of low-level code is needed to write a bootloader. An existing bootloader will be used: The GNU GRand Unified Bootloader (GRUB). The GRUB is a bootloader.

The operating system can be built using GRUB and loaded into the correct memory location. The code is laid out in a specific way when it’s compiled.

GRUB will transfer control to the operating system by jumping to a position in memory. Before the jump, GRUB will look for a magic number to ensure that it is actually jumping to an OS and not some random code.

Compiling the Operating System

Now we will Compiling the Operating System. For that save the followings in loader.s file.

global loader ; the entry symbol for ELF

MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant
FLAGS equ 0x0 ; multiboot flags
CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum
; (magic number + checksum + flags should equal 0)

section .text: ; start of the text (code) section
align 4 ; the code must be 4 byte aligned
dd MAGIC_NUMBER ; write the magic number to the machine code,
dd FLAGS ; the flags,
dd CHECKSUM ; and the checksum

loader: ; the loader label (defined as entry point in linker script)
mov eax, 0xCAFEBABE ; place the number 0xCAFEBABE in the register eax
jmp .loop ; loop forever

Then use the following command to compile this loader.s file,

nasm -f elf32 loader.s

Linking the Kernel

Next thing is linking the Kernel. For that save followings in link.ld file.

ENTRY(loader) /* the name of the entry label */

. = 0x00100000; /* the code should be loaded at 1 MB */

.text ALIGN (0x1000) : /* align at 4 KB */
*(.text) /* all text sections from all files */

.rodata ALIGN (0x1000) : /* align at 4 KB */
*(.rodata*) /* all read-only data sections from all files */

.data ALIGN (0x1000) : /* align at 4 KB */
*(.data) /* all data sections from all files */

.bss ALIGN (0x1000) : /* align at 4 KB */
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all bss sections from all files */

Then it should compile with this command,

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

The final executable will be called kernel.elf.

Copy this file stage2_eltorito file and after paste in to the folder that already contains loader.s and link.ld.

Building an ISO Image

For building an iso do following commands.

mkdir -p iso/boot/grub
cp stage2_eltorito iso/boot/grub/
cp kernel.elf iso/boot/

Then save followings in menu.lst file and Place the file it in the folder iso/boot/grub/.

Then the ISO image can then be generated with the following command:

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 \

In this section remember that the “\” is urgent. First time I’m not used this symbol. So that the terminal asked me to install another package. But after I tried correctly. So then don’t forgot that.

The ISO image os.iso now contains the kernel executable, the GRUB bootloader, and the configuration file.

Running Bochs

megs:            32
display_library: sdl
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
ata0-master: type=cdrom, path=os.iso, status=inserted
boot: cdrom
log: bochslog.txt
clock: sync=realtime, time0=local
cpu: count=1, ips=1000000

The bochsrc.txt file must contain above texts.

Then you can run Bochs with the following command:

bochs -f bochsrc.txt -q

So then you should have a window like this.

But it will be a trouble. If there happen a trouble you must change the display_library: sdl to display_library: sdl2 in file bochsrc.txt . Then the error will be fixed.

After quitting Bochs, display the log produced by Boch:

cat bochslog.txt

You should now see the contents of the registers of the CPU simulated by Bochs somewhere in the output. If you find RAX=00000000CAFEBABE or EAX=CAFEBABE(depending on if you are running Bochs with or without 64-bit support) in the output then your OS has successfully booted!

Finnaly you can refer my git hub for study those files.

Thank you!



Lahiru Chathuranga

Undergraduate from University of Kelaniya, Sri Lanka. And following Software Engineering course