Let’s Build an Own Operating System (PrimitiveOS)

Sachin Tharaka
8 min readJul 16, 2021

--

Welcome back!

I am ready to share some interesting articles weekly from today. If you see the topic, you already know this is going in a very interesting way.

First of all, developing an operating system is probably one of the most challenging things you can do on a computer. Composing an operating system requires a lot of knowledge about several complex areas within computer science. You need to understand how hardware works and be able to read and write the complex assembly language as well as a higher-level language (such as C, C++, or Pascal). Your mind has to be able to wrap itself around abstract theory and hold a myriad of thoughts.

Feel discouraged yet? Don’t fear! Because all of these things are also the things that make OS programming fun and entertaining

By end of this article series, you’ll be able to make your own operating system!

Week 01 --> setup_booting_os

1- Booting the OS

In this part, I will teach you how to build an operating system.

I first installed Ubuntu on a virtual machine. For that, I used Oracle Virtual Box.

Why do we need a virtual machine?

As apprentices, the main advantage we get from a virtual environment is to provide protected or isolated instances of the same operating system so that problems that occur in one enclave do not affect any other enclave or the host OS. A VM can be used for safety, security, or convenience. It can provide a sandbox that allows you to experiment, then reset to a previous state if you break it. Also, there are lots of other uses for VMs.

I used ubuntu as my virtual operating system. I am not familiar with virtual machines before this task as I followed this video for installing ubuntu as a virtual machine.

https://youtu.be/x5MhydijWmc

1.1 Quick Setup

Once Ubuntu is installed, the following packages should be installed using apt-get command.

You can simply install them by executing this command in your terminal.

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

1.2 Future comings

In this chapter let me know you some theoretical knowledge about operating systems and how it is working.

The operating system will be developed using the C programming language, using GCC (GNU Compiler Collection). We use C because developing an OS requires very precise control of the generated code and direct memory access. Other languages that provide the same features can also be used, but I am going with C.

1.3 Virtual Machine

When developing an OS it is very convenient to be able to run your code in a virtual machine instead of on a physical computer, since starting your OS in a virtual machine is much faster than getting your OS onto a physical medium and then running it on a physical machine. Bochs is an emulator for the x86 (IA-32) platform which is well suited for OS development due to its debugging features. Other popular choices are QEMU and VirtualBox. I am used Bochs.

2- Booting

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”. See the following figure for an example of the boot process:

Booting  process

2.1 BIOS

Basic Input Output System (BIOS) stored on a read-only memory chip on the motherboard of the computer. The task of this program is to start the computer system after its power is on. It will load some basic function instructions for hardware. And then it will transfer control to the bootloader.

2.2 Bootloader

This program will transfer control to us, the operating system. This program has two parts. The first part of will transfer control to the second part and that gives control of the PC to the operating system.

In this development process, we use an existing bootloader (GNU Grand Unified Bootloader-GRUB) because writing it from the beginning could be complex. This GRUB will load OS into a correct memory location. Then the control will transfer to the Operating System.

2.3 The Operating System

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. This magic number is part of the multiboot specification [19] which GRUB adheres to. Once GRUB has made the jump, the OS has full control of the computer.

3-Hello world to OS development

This section will describe how to implement the smallest possible OS that can be used together with GRUB. The only thing the OS will do is write 0xCAFEBABE to the eax register

Get into hands-on practices.

Important Point 01.

I recommend you maintain a separate folder for saving upcoming files etc. But after this, you have to open the terminal inside your folder path using the cd command.

My files in the Assignment folder as having to open the terminal in that location for executing commands.

Step 1

open your text editor and save the following code in a file called loader.s

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
.loop:
jmp .loop ; loop forever

Step 2

open terminal in loader.s File location

The file loader.s can be compiled into a 32 bits ELF [18] object file with the following command:

nasm -f elf32 loader.s

Step 3

3.2 Linking the Kernel

As we discussed before GRUB needs to load the kernel to the memory. For that, we need the following linking script. (That memory address should be larger than or equal to 1MB. Addresses lower than that will be used to GRUB, BIOS, and memory-mapped I/O.)

open your text editor and type the following linking script.

ENTRY(loader)                /* the name of the entry label */SECTIONS {
. = 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 */
}
}

Save the linker script into a file called link.ld.

Step 4

Open your terminal and can now be linked with the following command:

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

The final executable will be called kernel.elf.

Step 5

3.3 Obtaining GRUB

The GRUB version we will use is GRUB Legacy since the OS ISO image can then be generated on systems using both GRUB Legacy and GRUB 2. More specifically, the GRUB Legacy stage2_eltorito the bootloader will be used. This file can be built from GRUB 0.97 by downloading the source from:

Step 6

Copy the file stage2_eltorito to the folder that already contains loader.s and link.ld.

Step 7

3.4 Building an ISO Image

We will create the kernel ISO image with the program genisoimage. A folder must first be created that contains the files that will be on the ISO image. The following commands create the folder and copy the files to their correct places:(open your terminal in current folder location)

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

Step 8

A configuration file menu.lst for GRUB must be created. Open your text editor and type the following code

default=0
timeout=0
title os
kernel /boot/kernel.elf

Step 9

Place the file saved with the namemenu.lst in the folder iso/boot/grub/. The contents of the iso folder should now look like the following figure:

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

Step 10

The ISO image can then be generated with the following command:(open terminal and type following code)

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

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

Step 11

Running Bochs

Now we can run the OS in the Bochs emulator using the os.iso ISO image. Open a text editor and type the following code. Save the file name asbochsrc.txt

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

Important Point 02.

If you get an error at the end of Step 12 remember that you should come to this file and you should change display_library: to sdl2 instead of sdl.

Step 12

then you can run Bochs with the following command(open terminal and type following code):

bochs -f bochsrc.txt -q

Step 13

If you see some text on the Bochs emulator, containing “Booting os”, quit the emulator. If the emulator has no text on the screen, type continue in the terminal and hit enter. It will boot the OS.

We can now see Bochs starting and displaying a console with some information from GRUB on it.

End of these steps you should have filed as follows

Step 14

After quitting Bochs, display the log produced by Boch:

cat bochslog.txt

If RAX=00000000CAFEBABE or EAX=CAFEBABE is in the output(Bochs log file) then your OS has successfully booted!

Congratulations! you have done :)

In the beginning, looks like this is a very difficult task.. actually, it was. But after getting familiar with ubuntu and command lines now I think that it's going very interesting. Since next week we are getting into C language and we will meet quickly with the next article.

Bye.

#staysafe #stayconnected

Reference: https://littleosbook.github.io

--

--

Sachin Tharaka
Sachin Tharaka

Written by Sachin Tharaka

Software Engineering, University of Kelaniya, Sri Lanka

No responses yet