Let’s make your own operating system (#week 1)

Nimantha Gayan
7 min readJul 16, 2021

--

Did u ever think how to implement an OS? Let’s make a simple operating system. I’m going explain this as a weekly article series. So, this is our first article of this series.

Developing an operating system (OS) is not an easy task. I will put all implement steps in this articles series it’s helps you easy to understand the steps and you can easily develop your very own OS.

An operating system, or “OS,” is software that communicates with the hardware and allows other programs to run. It is comprised of system software, or the fundamental files your computer needs to boot up and function. Every desktop computer, tablet, and smartphone include an operating system that provides basic functionality for the device.

Now we are going begin our project. first of all, I recommend you setup a virtual machine because in the implementation part some steps affect your local machine,

This video may help you install VirtualBox: https://youtu.be/8mns5yqMfZk

This video may help you install ubuntu on virtual box: https://youtu.be/x5MhydijWmc

{load OS in Bootloader}

Quick setup

After installed Ubuntu in virtual box we need to install following packages using apt-get: Its simple open your terminal and type the code in below and press enter then starting to install the packages it takes few seconds.

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

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.

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

__attribute__((packed))

Booting

Basically, booting is the process of starting a computer. Once a computer is started there are no processes in its main memory so they have to load in order to execute them. This can be done by hardware or firmware in the CPU, or by a separate processor in the computer system. Booting is complete when the operative runtime system, typically the operating system, is attained. The booting is done by the software stored in the read-only memory (ROM) and the Growth in the capacity of ROM has allowed ever more elaborate start-up procedures to be implemented.

BIOS

Basic Input Output System (BIOS) is a small program, stored on a read-only memory chip on the motherboard, that starts when the PC is turned on.BIOS program was originally used to export some library functions for printing to the screen, reading keyboard input, etc. But Modern BIOS mainly runs some early diagnostics and then transfers control to the bootloader.

The Bootloader

The bootloader’s task is to transfer control to the operating system developers. It is usually split into two parts. The first part of the bootloader will transfer control to the second part, which finally gives control of the PC to the operating system.

Here we are not writing a bootloader as it involves writing a lot of low-level code that interacts with the BIOS. Therefore, we are going to use an existing bootloader called the GNU GRand Unified Bootloader (GRUB).

Using GRUB, the operating system can be built as an ordinary ELF executable, which will be loaded by GRUB into the correct memory location.

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 which GRUB adheres to. Once GRUB has made the jump, the OS has full control of the computer.

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

First, make a folder with your name of choice. Make sure to create all the files and sub-folders in this folder.

Implement OS

This section will describe how to implement of 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.

Compiling the Operating System

Create folder then open your text editor and paste the following code and save it as 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

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

nasm -f elf32 loader.s

(Open the terminal in the folder create before and run the upper code)

Linking the Kernel

Now the code needs to be linked to produce an executable file. GRUB will load the kernel at a memory address larger than or equal to 0x00100000 (1MB). Because GRUB itself, BIOS, and memory-mapped I/O all use addresses lower than 1 MB.

Again open your text editor and save the linker script below into a file called link.ld.

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 can now be linked with the following command:

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

now we can see final executable file called kernel.elf.

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.

The stage2_eltorito file can be downloaded from this link : https://github.com/whitequark/story-os/blob/master/grub/stage2_eltorito

Then copy the file stage2_eltorito to folder created before.

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) in the title part you can put your OS name in my case i put as windOS so it be like

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 (your_title.iso) now contains the kernel executable, the GRUB bootloader, and the configuration file.

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:

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

save the configuration in a file named bochsrc.txt

then you can run Bochs with the following command:

bochs -f bochsrc.txt -q

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 in the output then your OS has successfully booted!

--

--

Nimantha Gayan
Nimantha Gayan

Written by Nimantha Gayan

Software Engineering , University Of Kelaniya

No responses yet