How To Program Your Very Own Operating Systems (OS)- week1

Bhagya Wijenayake
7 min readJul 14, 2021

Ever wanted to make an Operating System? Ever wanted to know how command line operating systems work? Everything’s here!

So we’ll make a basic Operating System

By end of this tutorial, you’ll be able to make your cool operating system!

#1- Booting the OS

Today I will teach you how to build an operating system. Today is the first part of it.

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

this is my virtual machine
Use this video if you do not know how to install ubuntu on a virtual machine

1 Tools

1.1 Quick Setup

Once Ubuntu is installed, either physical or virtual, the following packages should be installed using apt-get:

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 Programming Languages

The operating system will be developed using the C programming language, using GCC. 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 this book will only cover C.

The code will make use of one type attribute that is specific for GCC:

__attribute__((packed))

1.3 Host Operating System

All the code examples assume that the code is being compiled on a UNIX-like operating system. All code examples have been successfully compiled using Ubuntu versions 11.04 and 11.10.

1.4 Build System

Make has been used when constructing the Makefile examples.

1.5 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. This book uses 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:

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 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 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

3.1 Compiling the operating system

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

open terminal in loarder.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

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.

The executable(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.

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 bootloader will be used. This file can be built from GRUB 0.97 by downloading the source from:

https://github.com/Bhagyawijenayake/Bhagya_OS/blob/main/stage2_eltorito

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

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

A configuration file menu.lst for GRUB must be created. This file tells GRUB where the kernel is located and configures some options:(open your text editor and type the following code)

default=0
timeout=0

title os
kernel /boot/kernel.elf

Place the file menu.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

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

For more information about the flags used in the command, see the manual for genisoimage.

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

3.5 Running Bochs

Now we can run the OS in the Bochs emulator using the os.iso ISO image. Bochs needs a configuration file to start and an example of a simple configuration file is given below(open text editor and type following code):

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

You might need to change the path to romimage and vgaromimage depending on how you installed Bochs. More information about the Bochs config file can be found on Boch’s website.

If you saved the configuration in a file named bochsrc.txt

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

bochs -f bochsrc.txt -q

If you see any error in your terminal try changing display_library: sdl to display_library: sdl2 . And try!

If you see some text on the bochs emulator, containing “Booting os”, quit the emulator. If 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.

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!

Reference: https://littleosbook.github.io/

Following is the Github repository for this process:https://github.com/Bhagyawijenayake/Bhagya_OS

--

--

Bhagya Wijenayake

Student at University of Kelaniya Sri Lanka- Software Engineering