How To Program Your Very Own Operating Systems (OS)- week1
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.
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