Notes about learning OS dev: [1] A minimal kernel
Below are list of references that I used to learn
What is the minimal setting/configuration we need to have so we can boot a minimal kernel?
Assuming we are using GRUB boot loader, it seems to me we need to have 4 files to be able to boot a minimal kernel.
…
├── Makefile
└── src
└── arch
└── x86_64
├── multiboot_header.asm
├── boot.asm
├── linker.ld
└── grub.cfg
Let’s go through these files one by one.
multiboot_header.asm
To know what is this, we need to know how boot works. (assuming we use BIOS firmware standard)
┌────────┐ ┌─────────┐ ┌────────────────────────────┐ ┌───────────┐
│ │ │ │ │ │ │ │
│Power On├──────►│Load BIOS├──────►│Switch control to Bootloader├───────►│Load kernel│
│ │ │ │ │ │ │ │
└────────┘ └─────────┘ └────────────────────────────┘ └───────────┘
In general, we will be using an existing bootloader, such as GRUB (which uses multiboot specification). In order to tell the bootloader our kernel will support the multiboot specification, we will need to have a multiboot header to let the bootloader that we are supporting multiboot.
section .multiboot_header
header_start:
dd 0xe85250d6 ; magic number (multiboot 2)
dd 0 ; architecture 0 (protected mode i386)
dd header_end - header_start ; header length
; checksum
dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) ; small hack to avoid compiler warning
; insert optional multiboot tags here
; required end tag
dw 0 ; type
dw 0 ; flags
dd 8 ; size
header_end: ; end of the file
boot.asm
Now the bootloader understands that we are using multiboot, what’s the next?
We need to add some code that bootloader can call. The way we do it is using boot.asm
global start ; export a lable
section .text ; default section for executing code
bits 32 ; specify the following lines are 32 bit execution
start:
; print `OK` to screen
mov dword [0xb8000], 0x2f4b2f4f
hlt ; tell CPU to stop
This basically defines a “function” but in assembly, it “prints” “OK” to screen and halt the CPU.
linker.ld
Although the previous two steps, we have some assembly code, but we just prepare some seperate bits for different purposes, but they are not working together yet. We will combine them into an ELF
executable so it can be run by the GRUB bootloader. The way we do this is through a linker.ld
file.
ENTRY(start) /*entry point of the kernel*/
SECTIONS {
. = 1M; /*conventional load address*/
.boot :
{
/* ensure that the multiboot header is at the beginning */
*(.multiboot_header)
}
.text :
{
*(.text)
}
}
The commands we can run to get the ELF executable.
> nasm -f elf64 multiboot_header.asm
> nasm -f elf64 boot.asm
> ld -n -o kernel.bin -T linker.ld multiboot_header.o boot.o
Note: It’s important to pass the -n (or –nmagic) flag to the linker, which disables the automatic section alignment in the executable. Otherwise the linker may page align the .boot section in the executable file. If that happens, GRUB isn’t able to find the Multiboot header because it isn’t at the beginning anymore.
After this step, we have an ELF executable called kernel.bin
. But how can we run
this kernel? This would require our to go to next step.
grub.cfg
The grub.cfg
is a config file of GRUB. And this file can also be used to create ISO. It is very simple to create an ISO.
Create a new ISO approach
- create a folder structure like this
isofiles
└── boot
├── grub
│ └── grub.cfg
└── kernel.bin
- What’s in the
grub.cfg
?
set timeout=0
set default=0
menuentry "my os" {
multiboot2 /boot/kernel.bin
boot
}
- run command
grub-mkrescue -o os.iso isofiles
. We will get anos.iso
to be used for boot.
For example, we can boot our OS using qemu-system-x86_64 -cdrom os.iso
Using host OS to boot
However, we can also load the ELF file on exist OS without creating an ISO file. To add the option to boot from our new OS, we can add the following text in /boot/grub/grub.cfg
menuentry "my os" {
insmod part_msdos
insmod ext2
set root='hd0,msdos4' # `df /boot/` -> `/dev/sda4` -> `hd0,msdos4`
multiboot2 /boot/kernel.bin
boot
}
Then we copy our kernel.bin
into /boot/
. If we reboot. This should work as well.
Conclusion
To summarize, we will mainly need 4 parts to be able to boot from a minimal kernel.
- we need to tell bootloader GRUB what specification we are using (eg. mutiboot specification) ->
multiboot.asm
- we need to have a kernel program that can be run ->
boot.asm
- we need to create a ELF executable (combine header + kernel program) ->
kernel.bin
- we need to have a config to tell GRUB how to install/run the OS we created ->
grub.cfg