Writing a Bootloader

What is a Bootloader?

A bootloader is a special program that is executed each time a bootable device is initialized by the computer during its power on or reset that will load the kernel image into the memory. This application is very close to hardware and to the architecture of the CPU. All x86 PCs boot in Real Mode. In this mode you have only 16-bit instructions. Our bootloader runs in Real Mode and our bootloader is a 16-bit program.

How this works?

When you switch on the PC the BIOS want to boot up an OS which must be found somewhere in hard disks, floppy disk, CDs, etc. The order in which BIOS searches an OS is user configurable. Next the BIOS reads the first 512 byte sector of the bootable disk. Usually a sector is 512 bytes in size. This is known as the Master Boot Record (MBR). BIOS simply loads the contents of the MBR into memory location โ€œ0x7c00โ€ and jumps to that location to start executing whatever code is in the MBR. Our bootloader should be 512 bytes in size as well.

Code 440 bytes
Disk signature 4 bytes
Null 2 bytes
Partition tables 64 bytes
MBR signature 2 bytes
Total 512 bytes

The boot process is:

  • Turn on your PC and BIOS executes
  • The BIOS seeks the MBR in boot order which is user configurable.
  • The BIOS loads a 512 byte boot sector into memory location โ€œ0x7c00โ€ from the specified media and begins executing it.
  • Those 512 bytes then go on to load the OS itself or a more complex bootloader.

BIOS Interrupts

These interrupts help OS and application invoke the facilities of the BIOS. This is loaded before the bootloader and it is very helpful in communicating with the I/O. Since we donโ€™t have OS level interrupts this is the only option that would be helpful.

For example to print a character to the screen using BIOS interrupt calls.

mov ah, 0x0e ; function number = 0Eh : Display Character
mov al, 'O' ; AL = code of character to display
int 0x10 ; call INT 10h, BIOS video service

This is a simple boot loader written in AT&T syntax.

/*
* Title: A Simple Bootloader in AT&T Assembly
* Author: Osanda Malith Jayathissa (@OsandaMalith)
* Website: http://OsandaMalith.wordpress.com
*/
.code16
.section .text
.global main
main:
/*
Disk description table, to make it a valid floppy
FAT12 file system format
*/
jmp _start
.byte 144 #NOP
.ascii "OsandaOS" #OEMLabel
.word 512 #BytesPerSector
.byte 1 #SectorsPerCluster
.word 1 #ReservedForBoot
.byte 2 #NumberOfFats
.word 224 #RootDirEntries (224 * 32 = 7168 = 14 sectors to read)
.word 2880 #LogicalSectors
.byte 0xf0 #MediumByte
.word 9 #SectorsPerFat
.word 18 #SectorsPerTrack
.word 2 #Sides
.long 0 #HiddenSectors
.byte 0 #LargeSectors
.byte 0 #DriveNo
.byte 0x29 #Signature (41 for Floppy)
.long 0x12345678 #VolumeID
.ascii "My First OS" #VolumeLabel
.ascii "FAT12 " #FileSystem
_start:
movw $0, %ax
movw %ax, %ss
movw %ax, %ds
movw %ax, %es
movw $string, %si
loop:
movb $0xe, %ah
movb (%si), %al
cmpb $0, %al
je done
int $0x10
addw $1, %si
jmp loop
done:
jmp done #infinite loop
string:
.ascii "Welcome to @OsandaMalith's First OS :)"
.byte 0
.fill 0x1fe - (. - main) ,1,0 #Pad remainder of boot sector with 0s
.word 0xaa55 #The standard PC boot signature

https://github.com/OsandaMalith/bootloader/blob/master/loader.S
Assemble and link the code. I have explicitly specified to load the text section to load at 0x7c00 and it will calculate the absolute addressing.

as --32 -o loader.o loader.S
ld -m elf_i386 loader.o --oformat=binary -o loader.bin  -Ttext 0x7c00

The same in NASM using Intel syntax.

%if 0;
* Title: A Simple Bootloader in NASM
* Author: Osanda Malith Jayathissa (@OsandaMalith)
* Website: http://OsandaMalith.wordpress.com
%endif;
BITS 16
jmp short _start ; Jump past disk description section
nop
; Disk description table, to make it a valid floppy
OEMLabel db "OsandaOS" ; Disk label
BytesPerSector dw 512 ; Bytes per sector
SectorsPerCluster db 1 ; Sectors per cluster
ReservedForBoot dw 1 ; Reserved sectors for boot record
NumberOfFats db 2 ; Number of copies of the FAT
RootDirEntries dw 224
LogicalSectors dw 2880 ; Number of logical sectors
MediumByte db 0F0h ; Medium descriptor byte
SectorsPerFat dw 9 ; Sectors per FAT
SectorsPerTrack dw 18 ; Sectors per track (36/cylinder)
Sides dw 2 ; Number of sides/heads
HiddenSectors dd 0 ; Number of hidden sectors
LargeSectors dd 0 ; Number of LBA sectors
DriveNo dw 0 ; Drive No: 0
Signature db 41 ; Drive signature: 41 for floppy
VolumeID dd 12345678h ; Volume ID: any number
VolumeLabel db "My First OS"; Volume Label: any 11 chars
FileSystem db "FAT12 " ; File system type: don't change!
_start:
mov ax, 07C0h ; move 0x7c00 into ax
mov ds, ax ; set data segment to where we're loaded
mov si, string ; Put string position into SI
call print_string ; Call our string-printing routine
jmp $ ; infinite loop!
string db "Welcome to @OsandaMalith's First OS :)", 0
print_string:
mov ah, 0Eh ; int 10h 'print char' function
.loop:
lodsb ; load string byte to al
cmp al, 0 ; cmp al with 0
je .done ; if char is zero, ret
int 10h ; else, print
jmp .loop
.done:
ret
times 510-($-$$) db 0 ; Pad remainder of boot sector with 0s
dw 0xAA55 ; The standard PC boot signature

https://github.com/OsandaMalith/bootloader/blob/master/loader.nasm
Assemble the file using binary as the format.

nasm -f bin -o loader.bin loader.nasm

If you use the file utility you will see that it’s a legit 1.4MB floppy Disk and a 32-bit boot sector.

$ file loader.bin 
loader.bin: DOS floppy 1440k, x86 hard disk boot sector

If you open our bootloader in a hex editor you will see our 512 size program and at the end there should be our boot signature 0xaa55.

After that convert the binary file to floppy image.

dd status=noxfer conv=notrunc if= loader.bin of=floppy.flp

You can also convert the binary file to an ISO using tools such as UltraISO, etc. You may burn and try in your PC instead of emulating.
Use Qemu to test our newly created bootloader ๐Ÿ™‚

qemu-system-i386 -fda floppy.flp


You can develop something like this ๐Ÿ™‚

Bootloader In C

We can use inline assembly using C to write a simple bootloader. Iโ€™ll be showing a simple example to print โ€œHelloโ€.

__asm__(".code16\n");
__asm__("jmpl $0x0000, $main\n");

void main() {
     __asm__ __volatile__("movb $'H' , %al\n");
     __asm__ __volatile__("movb $0x0e, %ah\n");
     __asm__ __volatile__("int  $0x10\n");

     __asm__ __volatile__("movb $'e' , %al\n");
     __asm__ __volatile__("movb $0x0e, %ah\n");
     __asm__ __volatile__("int  $0x10\n");

     __asm__ __volatile__("movb $'l' , %al\n");
     __asm__ __volatile__("movb $0x0e, %ah\n");
     __asm__ __volatile__("int  $0x10\n");

     __asm__ __volatile__("movb $'l' , %al\n");
     __asm__ __volatile__("movb $0x0e, %ah\n");
     __asm__ __volatile__("int  $0x10\n");

     __asm__ __volatile__("movb $'o' , %al\n");
     __asm__ __volatile__("movb $0x0e, %ah\n");
     __asm__ __volatile__("int  $0x10\n");

}

https://github.com/OsandaMalith/bootloader/blob/master/loader.c
This will be the linker script specified in the linker as โ€œtest.ldโ€

.ENTRY(main);
SECTIONS
{
    . = 0x7C00;
    .text : AT(0x7C00){ *(.text); }
    .sig : AT(0x7DFE) { SHORT(0xaa55); }
}

https://github.com/OsandaMalith/bootloader/blob/master/test.ld
After that compile using GCC and we generate only a object file and manually link using the linker specifying our linker script.

gcc -m32 -c -Os -march=i686 -ffreestanding -Wall -Werror -o loader.o loader.c
ld -m elf_i386 -static -Ttest.ld -nostdlib --nmagic -o test.elf test.o
objcopy -O binary loader.elf loader.bin
dd status=noxfer conv=notrunc if=loader.bin of=floppy.flp

After that boot our image using Qemu ๐Ÿ™‚

qemu-system-i386 -fda floppy.flp

Using BIOS interrupts you can write nice programs to the boot sector of your PC ๐Ÿ™‚

References

http://duartes.org/gustavo/blog/post/how-computers-boot-up/
http://wiki.osdev.org
http://mikeos.sourceforge.net/
https://en.wikipedia.org/wiki/BIOS_interrupt_call

Advertisements

23 thoughts on “Writing a Bootloader

  1. It is really an amazing article, I’ve compiled the code and ran it on qemu and it is working awesome.

    I’m failing to create iso of binary file using UltraISO, I get this error “Invalid or unknown image file format!” Can you help me out with this?

    Thanks in advance.

      • When opening .bin file with MagicIso I get this error “Can’t find the file or file isn’t CD image file!”.

        MagicIso -> File -> Open -> Error: “Can’t find the file or file isn’t CD image file!”.

        When I use MagicIso -> “Add File” to add loader.bin then I do get the ISO after saving but virtual box give error “No bootable medium found! System Halt.”

        Do I have to modify something in Disk description table. May be current one only works for floppy.

        I’m using this code:
        https://github.com/OsandaMalith/bootloader/blob/master/loader.nasm

  2. Hi Osanda
    Do you have any similar articles about building a bootloader for freescale kinetis controller with Kinetis Design Studio ? . If you have kindly comment me the link.
    Thanks & Regards
    Ganesh R
    (Electronics Undergrad)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s