Bootstrap Loader
The computer's BIOS attempt to boot the operating system from either floppy disk or the hard disk. When it boots from a floppy disk, it reads the content of the first sector of the floppy disk to memory at 0000:7C00 (segment:offset). If it is from a hard drive, it loads the Master Boot Sector to determine the active partition and then loads the boot code into the same address from the first sector of the partition. For easy of documentation, I will only discuss the Floppy Disk booting system. After loading your code there, BIOS does a far jump to 0000:7C00H. Some crappy BIOS does a far jump to 7C00:0000. This isn't a big concern because all that changes is your CS and IP which we do not need to concern about in our bootstrap loader.
In a floppy disk, one sector is a 512 byte continuous data storage location. That means, our boot code cannot be more than 512 bytes long. If you are a beginner, you might think that it is too small for any program. That is where the trick comes in. As it's name tells us, bootstrap loader does only only thing, it boot our computer, nothing else! Then it is our 'kernel' that does the super job. Our bootstrap loader reads out kernel from the floppy and put it in a specific location and then we jump from our 'bootstrap' to the 'kernel'. To make our life easier, we will use MS-DOS FAT12 file system when copying our files to floppy. Now there is one big problem. The first sector (logic sector 0) in an FAT12 file system is a reserved sector. When you format a floppy disk, MS-DOS places some data in that sector. If you change that data, the floppy will not read in DOS. So how will we put our boot code in the first sector without changing the values that are already in the sector? More that that we cannot just cut and paste our code into the floppy, because MS-DOS doesn't write anything in the first sector, not even the first file you write.
448 Bytes |
2 Bytes |
62 Bytes |
Boot Code |
Boot Structure |
Signature |
Now, like I said earlier, there are two ways to know what is in Boot Structure. The hardest way is to debug the first sector. The easiest way is to look below. The boot structure contains the following information in the following order. Remember, the values should be exact and must be in order.
Variable Name |
Size of Variable |
Value |
Short Jump Instruction | 3 Bytes | A jump to a label |
OEM Name | 8 Bytes | Any value |
Bytes Per Sector | 2 Bytes | 0x0200 |
Sectors Per Cluster | 1 Byte | 0x01 |
Reserved Sectors | 2 Bytes | 0x0001 |
# of FAT(s) | 1 Byte | 0x02 |
# of Root Directory Entries | 2 Bytes | 0x00E0 |
# of Sectors | 2 Bytes | 0x0B40 |
Media Byte | 1 Byte | 0xF0 |
Sectors Per FAT | 2 Bytes | 0x0009 |
Sectors Per Track | 2 Bytes | 0x0012 |
# of Heads | 2 Bytes | 0x02 |
# of Hidden Sectors | 4 Bytes | 0x00000000 |
Huge Sectors | 4 Bytes | 0x00000000 |
Drive Number | 1 Byte | 0x00 |
Flags | 1 Byte | 0x00 |
Boot Signature | 1 Byte | 0x29 |
Volume ID | 4 Bytes | Any value |
Volume Label | 11 Bytes | Any value |
File System | 8 Bytes | FAT12 |
I am not going to discuss what each means. Because that would take us into file-systems. Although the FAT12 file-system is a simple one, you can find better sources on internet about file systems. You don't need to know about FAT12 because you could read data through BIOS via sector, cylinder, track and head number. If you are good in assembly, you should be able to figure that out easily by looking in BIOS interrupts. Now, the last 2 bytes of the 512 bytes is called Signature and that should be set to 0xAA55. Now you have 448 bytes to code your boot loader. The only thing you should be doing in loader is read the 'kernel' off the floppy disk into a memory location. And then, jump there. Wow! so easy. For clarity, I will provide a small example. You should be able modify it with some additional code and should be running your boot code with no problem.
[Note: This is for NASM]
[ORG 0x0000]
[BITS 16]
jmp START
; jump instruction
OEM_ID
db "My OS"
BytesPerSector
dw 0x0200
SectorsPerCluster
db 0x01
ReservedSectors
dw 0x0001
TotalFATs
db 0x02
MaxRootEntries
dw 0x00E0
TotalSectorsSmall
dw 0x0B40
MediaDescriptor
db 0xF0
SectorsPerFAT
dw 0x0009
SectorsPerTrack
dw 0x0012
NumHeads
dw 0x0002
HiddenSectors
dd 0x00000000
TotalSectorsLarge
dd 0x00000000
DriveNumber
db 0x00
Flags
db 0x00
Signature
db 0x29
VolumeID
dd 0xFFFFFFFF
VolumeLabel
db "SIVA BOOT"
SystemID
db "FAT12 "
START:
cli
mov ax, 0x07C0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0x0400
; create stack [change if necessary]
mov ss, ax
mov sp, 0xFFFF
sti
mov si, msgLoading
; Print out the message
call PrintMsg
;////// TO DO ///// [You should write your kernel loading code here]
; .................................................
;/////// Loading Code Ends here
PrintMesg:
lodsb
; load next character [ES:SI]
or al, al
; If 00H, then exit
jz .Print2
mov ah, 0x0E
mov bh, 0x00
mov bl, 0x07
int 0x10
; BIOS interrupt
jmp PrintMesg
; Continue to next character
.Print2:
ret
;/////// Data Section //////
msgLoading db "Please Wait..... Loading Kernel.......", 00H
times 510-($-$$) DB 0
; This make sure that your code is 512 bytes long
dw 0xAA55
; The boot signature
;/////////////////// Boot Code Ends ///////////////////////
There will be one thing that looks the same in everybody's OS, the Boot Code. It is becoming a long lasting tradition to load kernel from the boot code. The only difference is from there. You should finish the code there and should have the code running before going further. See you in next topic.