BootLoader and OS
1 Bootloader
1.1 Summary
- BIOS reads first 512 bytes
- Check for magic 2-byte at end
- Loads the 512 bytes
1.2 Theory
- The 512 bytes is in the first boot sector of the bootable drive.
- The magic 2-bytes is
0x55 0xaa
e9 fd ff 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 29 more lines with sixteen zero-bytes each ]
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa1.3 Implement
Using nasm x86 asm
sudo apt install nasm
sudo apt-get install qemu-system
nasm -fbin boot.asm -o boot.bin
qemu-system-x86_64 -nographic boot.bin Zero out 510 bytes, then add the 2-byte bootloader identifier 0x55 0xaa
jmp $
times 510-($-$$) db 0
db 0x55, 0xaaBooting from Hard Disk...jmp $ means jump back to this line AKA infinite loop.
single dollar = current address
double dollar = section start
(\$-\$\$) = (current addr - section start) = length of previous code
db := define byte
db 0x55, 0xaa is equivalent to dw 0xaa55
510 - length of previous code = allows us to fill the memory to 510 bytes
BIOS (vs UEFI)
Boot into the bootsector we enter into “REAL MODE”.
“REAL MODE” is a 16 bit mode assisted by BIOS.
- Switch to teletype mode by modifying a register then calling a BIOS interrupt
mov ah, 0x0e
mov al, 'A'
int 0x10Printing ‘A’s. ’A’ = 65 in dec = 0x41 in hex = 0b1000001 in binary(0b means binary)
mov ah, 0x0e
mov al, 'A'
int 0x10
mov ah, 0x0e
mov al, 65
int 0x10
mov ah, 0x0e
mov al, 0x41
int 0x10
mov ah, 0x0e
mov al, 0b1000001
int 0x102 Disks
Memory is divided into 512 chunks.
Cylinder = [0..) Head = [0..) Sector = [1..)
The location of each 512 chunk of memory on a disk is determined by
(Cylinder,Head,Sector)
The first chunk of memory = “Boot Sector” at (C=0,H=0,S=1)
2.0.1 OS
We need to write a bootloader.
To compiler
3 Diassembly
Here you can see a diasssembler used against boot.bin with “x86 Real mode 16 bit” since when we compiled boot.bin, our nasm compiler defaults to x86 16 bit real mode Note: press D to decompile
org 0x7C00 ;this puts 0x7C at 00000024 if you open the *.bin
bits 16 ;BIOS boots the bootloader into 16 bit realmode
%define ENDL 0x0D, 0x0A
start:
jmp main
puts:
;save register we will modify
push si
push ax
push bx
.loop:
lodsb ; loads a byte in the al register, then increments si
or al, al
jz .done
mov ah, 0x0e
mov bh, 0
int 0x10
jmp .loop
.done:
pop bx
pop ax
pop si
ret
main:
;;GoodToKnow: segment registers are cs, ss, ds, es, fs, gs
;;cs = currently running code segment
;;ds = data segment
;;es,fs,gs = (extra) data segment
;;ss = stack segment
;setup data segment
mov ax, 0
mov ds, ax ;;segment register ds
mov es, ax ;;segment register es
;setup stack segment
mov ss, ax ;;segment register ss
mov sp, 0x7C00 ;;pointer register sp
;print message
mov si, msg_hello
call puts
hlt
msg_hello: db "hello World", ENDL, 0
;bootloader is the first 512 bytes and
;the last 0x55 0xaa tells the BIOS it is the bootloader
times 510-($-$$) db 0
db 0x55, 0xaa