Developing your own operating system (Part 01)

Sheran Randika
4 min readOct 1, 2022

--

Creating an operating system (OS) is a difficult task. This article will support you in configuring your development environment and launching a very small operating system. The following points should be well-understood by you before beginning the implementation.

Virtual Machine

We must first set up a virtual machine on our 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, being able to run your code in a virtual machine rather than on a personal machine is very convenient when developing an OS. Then, install Ubuntu on the virtual machine.

After installing Ubuntu, the following packages should be installed

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

The C programming language and GCC will be used to create the operating system. One type attribute unique to GCC will be used by the code.

Booting

Beginning the booting process is the motherboard-stored Basic Input Output System, a small program. Following that, the BIOS transfers control to the Bootloader, which then transfers control to the Operating System.

The Operating System

This article will explain how to create the smallest operating system that can be used in conjunction with GRUB. The only thing the OS will do is write0xCAFEBABE to the eax register.

First, create a folder with your preferred name. Ensure that all of the folder’s files and subfolders are created.

Compiling the Operating System

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

With the command listed below, the file loader.s can be converted into a 32-bit ELF object file.

nasm -f elf32 loader.s

Now the folder contains the loader.o file that was generated.

Linking the Kernel

The following linking script can be used to load the kernel into GRUB’s memory.

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 */
}
}

The executable can now be linked with the below command.

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

You will now see the final executablekernel.elf in the folder.

Obtaining GRUB

Since both GRUB 2 and GRUB Legacy systems can create the OS ISO image, GRUB Legacy is the version of GRUB we’ll use. More specifically, the stage2_eltoritobootloader from GRUB Legacy will be used.

The stage2_eltorito file can be downloaded here.

https://github.com/Sheran-Randika/SR_OS/tree/setup_booting_os

Next, transfer the stage2_eltorito file to our folder.

Building an ISO Image

We will use the program genisoimage to generate the kernel ISO image. Initially, a folder containing the files that will be on the ISO image needs to be created.

The folder is made and the files are copied to their proper locations using the commands below:

   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

There needs to be a menu.lstconfiguration file for GRUB. This file configures some settings and instructs GRUB where to find the kernel:

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

The following command can then be used to create the ISO image:

    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

Running Bochs

Now we can use the os.iso ISO image to run the OS in the Bochs emulator. The code below should be saved in a text file called bochrsrc.txt.

    megs:            32
display_library: sdl2
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

In the final step, you can run Bochs with the following command:

bochs -f bochsrc.txt -q

Type continue in the terminal. At this point, Bochs ought to be booting up and displaying a console with data from GRUB on it.

So, this is the first part of the Developing of my simple operating system. I hope to see you again in the next part!

Thank you!

--

--