Develop Your Own Operating System
First of all let’s decide what tools and technologies we going to use in our implementation so that we can get started.
1 . 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 article uses Bochs.
By using a virtual machine we cannot ensure that our OS works on real, physical hardware. The environment simulated by the virtual machine is designed to be very similar to their physical counterparts, and the OS can be tested on one by just copying the executable to a CD and finding a suitable machine.
2. Booting
BIOS: When the PC is turned on, the computer will start a small program that adheres to the Basic Input Output System (BIOS) standard. This program is usually stored on a read-only memory chip on the motherboard of the PC. The main responsibility of the BIOS program was to export some library functions for printing to the screen, reading keyboard input, etc.
The Bootloader: The computer’s boot process begins with a program called a bootloader. The BIOS program will transfer control of the PC to a bootloader. The bootloader’s task is to transfer control to us, the operating system developers In Linux, the most-used boot loader is the Grand Unified Boot Loader (GRUB), available in two versions: GRUB version 1 (also known as GRUB Legacy) and GRUB version 2. GRUB Legacy is the older of the two boot loaders., and our code. However, due to some restrictions of the hardware and because of backward compatibility, the bootloader is often 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.
3. Compiling the operating system
In here we use little bit of assembly code. This program will write a very specific number 0xCAFEBABE
to the eax
register. Save the following code in a file called loader.s
The file loader.s
can be compiled into a 32 bits ELF [18] object file with the following command:
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.
4. Linking the Kernel
The code must now be linked to produce an executable file, which requires some extra thought compared to when linking most programs. We want GRUB to load the kernel at a memory address larger than or equal to 0x00100000
(1 megabyte (MB)), because addresses lower than 1 MB is used by GRUB itself, BIOS, and memory-mapped I/O. Therefore, the following linker script is needed (written for GNU 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
4. 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 bootloader will be used.
Download stage2_eltorito
from GitHub
Copy the file stage2_eltorito
to the folder that already contains loader.s
and link.ld
.
5. Building an ISO Image
The executable must be placed on a media that can be loaded by a virtual or physical machine. In this book, we will use ISO image files as the media, but one can also use floppy images, depending on what the virtual or physical machine supports.
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:
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:
default=0
timeout=0title 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 screenshot:
The ISO image can then be generated with the following command:
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
6. Running Bochs
Now we can run the OS in the Bochs emulator using the os.iso
ISO image. Save the following code in a 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. You should now see Bochs starting and displaying a console with some information from GRUB on it.
See you in Second tutorial
Thank you.