User:Glauxosdev/Tutorials/Bootloader
In this tutorial we will learn how to create a bootloader that loads some sectors and executes them.
Take 0x01
A bootloader is a real mode program that resides in the first sector of the disk; let it be on a hard disk or on a USB flash disk. The BIOS loads only the bootsector, so to start we have only 512 bytes, unless we load more sectors ourselves. The bootsector is loaded at address 0x00007C00, so using real mode segmentation the address is translated to 0x0000:0x7C00 or 0x07C0:0x0000. So for a start we have this code:
bits 16 ; tell the assembler we want 16 bit code
org 0x7C00 ; tell the assembler to add 0x7C00 offset to labels, segments will be zeroed
start:
.setup_segments:
mov ax, 0 ; pass 0 value to ax, as segments can't be set with immediate values
mov ds, ax ; ds = ax = 0
mov es, ax ; es = ax = 0
mov fs, ax ; fs = ax = 0
mov gs, ax ; gs = ax = 0
mov ss, ax ; ss = ax = 0
.setup_stack:
mov sp, 0x7C00 ; stack grows downwards from the start of the bootloader
.hang:
jmp $ ; jump infinitely to the same instruction
end_boot_sector:
times 510 - ($ - $$) db 0
db 0x55
db 0xAA
Save this file as "boot.asm" and assemble it with nasm:
nasm -f bin -o boot.bin boot.asm
Then copy the resulting binary to the first sector of a USB flash disk, where xxx indicates the block device corresponding to the USB flash disk.
sudo dd if=boot.bin of=/dev/xxx
Note: Don't try to copy the binary to the hard disk, as it will become unbootable.
Reboot your computer and you will see a blinking cursor. Congratulations, you have made a bootable application. But it doesn't much. So, let's continue.
Take 0x02
Let's add some more code so we can load a sector:
bits 16 ; tell the assembler we want 16 bit code
org 0x7C00 ; tell the assembler to add 0x7C00 offset to labels, segments will be zeroed
start:
.setup_segments:
mov ax, 0 ; pass 0 value to ax, as segments can't be set with immediate values
mov ds, ax ; ds = ax = 0
mov es, ax ; es = ax = 0
mov fs, ax ; fs = ax = 0
mov gs, ax ; gs = ax = 0
mov ss, ax ; ss = ax = 0
.setup_stack:
mov sp, 0x7C00 ; stack grows downwards from the start of the bootloader
.save_boot_device_number:
mov byte [bootdev], dl ; boot device number is passed in dl by BIOS
.check_lba_extensions:
mov ah, 0x41
mov bx, 0x55AA
int 0x13
jc .hang ; carry flag is set if bios lba extensions don't exist
cmp bx, 0xAA55 ; bx = 0xAA55 if bios lba extensions exist
jne .hang
.load_next_sector:
mov eax, 1 ; load sector index 1
mov bx, next_sector ; at address indicated by next_sector label
mov cx, 1 ; load only 1 sector
call read_sectors ; call the routine to load sectors
jmp 0x7E00 ; jump to newly loaded sector
.hang:
jmp $ ; jump infinitely to the same instruction
read_sectors:
.save_registers:
pusha ; registers are preserved
.build_disk_address_packet:
mov si, 0x8000 ; disk address packet is at 0x00008000 (ds = 0)
mov word [ds:si], 0x10 ; size of packet is 16 bytes
mov word [ds:si + 2], cx ; sector count is in cx
mov word [ds:si + 4], bx ; memory offset is in bx
mov word [ds:si + 6], fs ; memory segment is in fs
mov dword [ds:si + 8], eax ; low starting lba sector is in eax
mov dword [ds:si + 12], 0
.invoke_disk_int:
mov ah, 0x42 ; bios extended read is used here
mov dl, byte [bootdev] ; boot device will be read
int 0x13 ; bios disk interrupt is invoked
.return:
popa ; registers are restored
ret ; return
variables:
bootdev db 0
end_boot_sector:
times 510 - ($ - $$) db 0
db 0x55
db 0xAA
; SECTOR 0x01 START ===================================================================
next_sector:
.print_letter_A:
mov bx, 0xB800 ; video memory resides at 0x000B8000
mov es, bx
mov bx, 0 ; print letter "A" at the first row and column
mov byte [es:bx], 65
.hang:
jmp $ ; jump infinitely to the same instruction
end_second_sector:
times 512 - ($ - next_sector) db 0
If you assemble this file and boot a USB flash disk with the resulting binary, you will see a letter "A" at the top-left corner of the screen, except if your computer is such old so it doesn't support BIOS LBA extensions.