1/* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Bootsector 4 * FILE: boot/freeldr/bootsect/faty.S 5 * PURPOSE: Combined FAT12, FAT16 and FAT32 boot sector 6 * PROGRAMMERS: Brian Palmer 7 * Timo Kreuzer 8 */ 9 10#define DISKREADBUFFER HEX(8E000) 11 12/* 13 * Layout of a FAT volume: 14 * 15 * |--------------------------------------------------------- 16 * | * BootSector | 17 * | * FS Information Sector (FAT32 only) | ReservedSectors 18 * | * ... more reserved sectors ... | 19 * |-------------------------------------------------------- 20 * | * FAT 1 | NumberOfFats 21 * | * FAT 2 | * 22 * | * [more FATs] | SectorsPerFat 23 * |--------------------------------------------------------- 24 * | * Root Directory (FAT12/FAT16 only) | MaxRootEntries / 16 25 * |--------------------------------------------------------- 26 * | * File data | 27 * | .... | 28 * |---------------------------------------- 29 */ 30 31/* INCLUDES ******************************************************************/ 32 33#include <asm.inc> 34#include <freeldr/include/arch/pc/x86common.h> 35 36#define ADDRESS_FOR_DIRENTRIES HEX(10000) 37 38SizeOfDataArea = 32 39 40/* Put the stack below the data area */ 41BootSectorStackTop = (HEX(7c00) - SizeOfDataArea) 42 43/* Data area offsets for uninitialized data */ 44DataAreaStart = BootSectorStackTop + 0 /* dword */ 45#ifndef FAT32 46RootDirStartSector = BootSectorStackTop + 4 /* dword */ 47#endif 48BiosCHSDriveSize = BootSectorStackTop + 8 /* dword */ 49LBASectorsRead = BootSectorStackTop + 12 /* dword */ 50ReadSectorsOffset = BootSectorStackTop + 16 /* word */ 51ReadClusterOffset = BootSectorStackTop + 18 /* word */ 52PutCharsOffset = BootSectorStackTop + 20 /* word */ 53 54/* Macro for bp relative memory access to reduce code size */ 55#define BP_REL(x) ss:[bp + x - BootSectorStackTop] 56 57/* The code starts at 0x7c00 */ 58// org 7c00h 59 60.code16 61 62/****************************************************************************** 63 * BIOS Parameter Block (BPB) * 64 ******************************************************************************/ 65/* We have 3 bytes at the entry point to jump over the data area */ 66start: 67 jmp short main // FIXME: When compiling FAT32, assembler will complain 68 // that the label is too far... Need investigation! 69 nop 70 71/* Here starts the BIOS Parameter Block (BPB) data. 72 The real data will be copied during install */ 73OEMName: 74 .ascii "FrLdr1.0" 75BytesPerSector: 76 .word 512 77SectorsPerCluster: 78 .byte 0 79ReservedSectors: 80 .word 32 81NumberOfFats: 82 .byte 2 83MaxRootEntries: 84 .word 0 // Always zero for FAT32 volumes 85TotalSectors: 86 .word 0 // Always zero for FAT32 volumes 87MediaDescriptor: 88 .byte HEX(0f8) 89SectorsPerFat: 90 .word 0 // Always zero for FAT32 volumes 91SectorsPerTrack: 92 .word 0 93NumberOfHeads: 94 .word 0 95HiddenSectors: 96 .long 0 97TotalSectorsBig: 98 .long 0 99 100/* Extra data for FAT32 volumes */ 101#ifdef FAT32 102SectorsPerFatBig: 103 .long 0 104ExtendedFlags: 105 .word 0 106FSVersion: 107 .word 0 108RootDirStartCluster: 109 .long 0 110FSInfoSector: 111 .word 0 112BackupBootSector: 113 .word 6 114Reserved1: 115 .space 12, 0 116#endif // FAT32 117 118BootDrive: 119 .byte 0 120Reserved: 121 .byte 0 122ExtendSig: 123 .byte HEX(29) 124SerialNumber: 125 .long 0 126VolumeLabel: 127 .ascii "NO NAME " 128FileSystem: 129 .ascii "FATxx " 130 131 132/****************************************************************************** 133 * String data * 134 ******************************************************************************/ 135 136filename: 137 .ascii "FREELDR SYS" 138 139msgBootFailure: 140 .ascii "Load failed!", CR, LF, NUL 141 142msgAnyKey: 143 .ascii "Press any key to reboot...", NUL 144 145 146/****************************************************************************** 147 * Main code entry * 148 * Input: DL = Boot drive * 149 ******************************************************************************/ 150main: 151 /* First setup the segment registers */ 152 xor ax, ax 153 mov ds, ax 154 mov ss, ax 155 156 /* Load the stack pointer */ 157 mov sp, BootSectorStackTop 158 159 /* Load bp for relative memory access, which saves us some bytes of code 160 size, when used with 32 bit instructions */ 161 mov bp, sp 162 163 /* Load the boot drive from the BPB into al */ 164 mov al, byte ptr ds:[BootDrive] 165 166 /* Check if it's valid */ 167 cmp al, HEX(0ff) 168 je .SaveBootDrive 169 170 /* Copy it into dl */ 171 mov dl, al 172 173.SaveBootDrive: 174 /* Save the bootdrive in the BPB */ 175 mov byte ptr ds:[BootDrive], dl 176 177 178/****************************************************************************** 179 * Get drive parameters * 180 ******************************************************************************/ 181 182 /* Call INT 13 to get the drive parameters: 183 AH = 08h 184 DL = drive (bit 7 set for hard disk) 185 ES:DI = 0000h:0000h to guard against BIOS bugs */ 186 xor di, di 187 mov ah, 8 188 int HEX(13) 189 190 /* Return from INT 13h/08h: 191 CF set on error -> AH = status (07h) 192 CF clear if successful -> AH = 00h 193 AL = 00h on at least some BIOSes 194 BL = drive type (AT/PS2 floppies only) 195 CH = low eight bits of maximum cylinder number 196 CL = bits 0:5 maximum sector number, bits 7:8 high two bits of maximum cylinder number 197 DH = maximum head number 198 DL = number of drives 199 ES:DI -> drive parameter table (floppies only) */ 200 201 /* Check for failure */ 202 jc BootFailure 203 204 205/****************************************************************************** 206 * Calculate drive size * 207 ******************************************************************************/ 208 209 movzx ebx, ch // Put the low 8-bits of the cylinder count into EBX 210 mov bh, cl // Put the high 2-bits in BH 211 shr bh, 6 // Shift them into position, now BX contains the cylinder count 212 213 and cl, HEX(3f) // Mask off cylinder bits from sector count 214 movzx ecx, cl // Move the sectors per track into ECX 215 216 movzx eax, dh // Move the heads into EAX 217 218 inc eax // Make it one based because the bios returns it zero based 219 inc ebx // Make the cylinder count one based also 220 mul ecx // Multiply heads with the sectors per track, result in edx:eax 221 mul ebx // Multiply the cylinders with (heads * sectors) [stored in edx:eax already] 222 223 // We now have the total number of sectors as reported 224 // by the bios in eax, so store it in our variable 225 mov dword ptr BP_REL(BiosCHSDriveSize), eax 226 227 228/****************************************************************************** 229 * Load the FAT * 230 ******************************************************************************/ 231 232 /* Load the number of first sector of the FAT into eax */ 233 movzx eax, word ptr BP_REL(ReservedSectors) 234 add eax, dword ptr BP_REL(HiddenSectors) 235 236 /* Load sector count into ecx */ 237#ifdef FAT32 238 mov ecx, dword ptr BP_REL(SectorsPerFatBig) 239#else 240 movzx ecx, word ptr BP_REL(SectorsPerFat) 241#endif 242 243 /* Save FAT sector and size for later use */ 244 pushad 245 246 /* Point ES:DI to the memory that is later the disk read buffer for freeldr. 247 This way we cannot overwrite our FAT with freeldr data */ 248 mov bx, DISKREADBUFFER / 16 249 mov es,bx 250 xor di, di 251 252 /* Read the sectors */ 253 call ReadSectors 254 255 /* Restore FAT sector and size */ 256 popad 257 258 259/****************************************************************************** 260 * Get root directory / data area start * 261 ******************************************************************************/ 262 263 /* Copy reserved + hidden sectors to EBX */ 264 mov ebx, eax 265 266 /* Calculate (NumberOfFats * SectorsPerFat) */ 267 movzx eax, byte ptr BP_REL(NumberOfFats) 268 mul ecx 269 270 /* Add reserved sectors and hidden sectors */ 271 add eax, ebx 272 273#ifndef FAT32 274 /* Save the starting sector of the root directory */ 275 mov dword ptr BP_REL(RootDirStartSector), eax 276 277 /* Calculate number of sectors for the root dir: 278 sectors = MaxRootEntries * 32 / 512 (rounded up!) */ 279 movzx ebx, word ptr BP_REL(MaxRootEntries) 280 add ebx, 15 281 shr ebx, 4 282 283 /* Add the root dir start sector and save it as DataAreaStart */ 284 add ebx, eax 285 mov dword ptr BP_REL(DataAreaStart), ebx 286#else 287 mov dword ptr BP_REL(DataAreaStart), eax 288 289 /* On FAT32 volumes the root dir start cluster is stored in the BPB */ 290 mov eax, dword ptr BP_REL(RootDirStartCluster) 291#endif 292 293 294/****************************************************************************** 295 * Search the root directory for freeldr * 296 ******************************************************************************/ 297.SearchForFreeldr: 298 299 /* Load ES with the segment where we put the dir entries */ 300 mov bx, ADDRESS_FOR_DIRENTRIES / 16 301 mov es, bx 302 303 /* Set the address offset to 0 */ 304 xor di, di 305 306#ifdef FAT32 307 /* Read the dir cluster. This loads the next cluster into EAX */ 308 call ReadCluster 309 310 /* Calculate the numer of dir entries in this cluster: 311 dx = SectorsPerCluster * 512 / 32 */ 312 movzx dx, byte ptr ds:[SectorsPerCluster] 313 shl dx, 4 314#else 315 /* Set the number of sectors to read to 1 */ 316 xor cx, cx 317 inc cx 318 319 /* Read the sector, but preserve ES */ 320 push es 321 call ReadSectors 322 pop es 323 324 /* Set entry count to entries per sector */ 325 mov dx, (512 / 32) 326#endif 327 328 /* Load the start offset of the dir entries into ebx */ 329 xor bx, bx 330 331.CheckDirEntry: 332 /* Load the address of the name into di */ 333 mov di, bx 334 335 /* If the first byte of the entry is 0 then we have reached the end */ 336 cmp byte ptr es:[di], ch 337 jz BootFailure 338 339 /* Compare with freeldr file name */ 340 mov si, offset filename 341 mov cx, 11 342 repe cmpsb 343 344 /* Check if we found the file */ 345 jz .FoundFreeLoader 346 347 /* File didn't match, go to next entry */ 348 add bx, 32 349 350 /* Decrement entry count and check if we reached the end */ 351 dec dx 352 jnz .CheckDirEntry 353 354#ifdef FAT32 355 /* Check to see if this was the last cluster in the chain */ 356 cmp eax, HEX(0ffffff8) 357 jnb BootFailure 358#endif 359 360 /* Repeat the search process with the next sector / cluster. 361 eax is already incremented in ReadSectors / ReadCluster */ 362 jmp .SearchForFreeldr 363 364 365/****************************************************************************** 366 * Load freeldr * 367 ******************************************************************************/ 368.FoundFreeLoader: 369 370 /* Load the cluster number of freeldr into eax */ 371#ifdef FAT32 372#error unsupported 373#else 374 movzx eax, word ptr es:[bx + HEX(1A)] 375#endif 376 377 /* Load es:di with the freeldr start address */ 378 mov dx, FREELDR_BASE / 16 379 mov es, dx 380 xor di, di 381 382.LoadNextCluster: 383 /* Load the cluster to the current address. EAX is adjusted to the next 384 cluster and ES is adjusted for the next read */ 385 call ReadCluster 386 387 /* Check if this is the last cluster in the chain */ 388#if defined(FAT32) 389 cmp eax, HEX(0ffffff8) 390#elif defined(FAT12) 391 cmp ax, HEX(0ff8) 392#else 393 cmp ax, HEX(0fff8) 394#endif 395 jb .LoadNextCluster 396 397 /* Load boot drive into DL, boot partition into DH */ 398 mov dl, byte ptr ds:[BootDrive] 399 mov dh, byte ptr ds:[BootPartition] 400 401 /* Now the complete freeldr imag is loaded. 402 Jump to the realmode entry point. */ 403 ljmp16 0, FREELDR_BASE 404 405 406 407BootFailure: 408 mov si, offset msgBootFailure 409 call PutChars 410 411 412Reboot: 413 /* Output "Press any key to reboot" message */ 414 mov si, offset msgAnyKey 415 call PutChars 416 417 /* Wait for a keypress */ 418 xor ax, ax 419 int HEX(16) 420 421 /* Reboot */ 422 int HEX(19) 423 424 425/****************************************************************************** 426 * PROCEDURE ReadCluster * 427 * Input: EAX = Cluster number, ES:DI = Target * 428 * Modifies: EAX (next cluster number), BX, DX (undefined) * 429 ******************************************************************************/ 430ReadCluster: 431 432 pushad 433 434 // StartSector = ((Cluster - 2) * SectorsPerCluster) + SectorsForFat + ReservedSectors + HiddenSectors 435 // StartSector = ((Cluster - 2) * SectorsPerCluster) + DataAreaStart 436 437 /* Substract 2 */ 438 dec eax 439 dec eax 440 441 /* Multiply with SectorsPerCluster */ 442 movzx ecx, byte ptr BP_REL(SectorsPerCluster) 443 mul ecx 444 445 /* Add DataAreaStart */ 446 add eax, dword ptr BP_REL(DataAreaStart) 447 448 /* Call ReadSectors. EAX = SectorNumber, ECX = SectorsPerCluster */ 449 call ReadSectors 450 451 /* Restore the cluster number */ 452 popad 453 454 /* Save ES */ 455 push es 456 457#if defined(FAT32) 458#error FAT32 not implemented 459#elif defined(FAT12) 460#error FAT12 not implemented 461#else 462 /* DX:AX = AX * 2 (since FAT16 entries are 2 bytes) */ 463 mov bx, 2 464 mul bx 465 466 /* Shift DX, so that it is the segment offset: DX = DX * (64K / 16) */ 467 shl dx, 12 468 469 /* Put segment address of FAT into ES */ 470 add dx, DISKREADBUFFER / 16 471 mov es, dx 472 473 /* Put the FAT entry offset into EBX for indirect mov */ 474 mov bx, ax 475 476 /* Put the content of the FAT entry into AX */ 477 mov ax, es:[bx] 478#endif 479 480 /* Restore ES and return */ 481 pop es 482 ret 483 484 485/****************************************************************************** 486 * PROCEDURE ReadSectors * 487 * Input: EAX = Sector start number, ECX = number of sectors, ES:DI = Target * 488 * Modifies: EAX (incremented by sector count), CX = 0, ES (incremented), * 489 * EBX undefined * 490 ******************************************************************************/ 491ReadSectors: 492 /* We could possibly also implement CHS, but it's currently unimplemented */ 493//jmp $ 494ReadSectorsLBA: 495 496 /* Copy number of sectors to ebx */ 497 movzx ebx, cx 498 499 /* Since the LBA calls only support 0x7F sectors at a time, 500 we will limit ourselves to 64 */ 501 cmp bx, 64 502 jbe .ReadSectorsLBA2 503 mov bx, 64 504 505.ReadSectorsLBA2: 506 507 /* Save logical sector number & sector count */ 508 pushad 509 510 /* Setup the disk address packet on the stack */ 511 .byte HEX(66) // size overwrite prefix for next push 512 push 0 // Put 64-bit logical block address (high part) on stack 513 push eax // Put 64-bit logical block address (low part) on stack 514 push es // Put transfer segment on stack 515 push di // Put transfer offset on stack 516 push bx // Set transfer count (for this round) 517 push 16 // Set size of packet to 16 518 519 /* Point si to the disk address packet on stack */ 520 mov si, sp 521 522 /* Set the drive number */ 523 mov dl, byte ptr ds:[BootDrive] 524//jmp $ 525 /* Call INT 13h, AH = 42h - Extended Read 526 Input: ... 527 Modifies: ... */ 528 mov ah, HEX(42) 529 int HEX(13) 530//jmp $ 531 /* Check for failure */ 532 jc BootFailure 533 534 /* Remove disk address packet from stack */ 535 add sp, 16 536 537 /* Adjust ES to point to the next sector */ 538 shl bx, 5 539 mov ax, es 540 add ax, bx 541 mov es, ax 542 543 /* Restore sector count & logical sector number */ 544 popad 545 546 /* Adjust the sector number to the next sector we need to read 547 by adding the number of sectors that we read */ 548 add eax, ebx 549 550 /* Adjust remaining sectors */ 551 sub cx, bx 552 jnz ReadSectorsLBA 553 554 /* return */ 555 ret 556 557 558 559/****************************************************************************** 560 * PROCEDURE PutChars * 561 * Input: ESI = Points to string to be printed * 562 * Modifies: AL, AH, SI * 563 ******************************************************************************/ 564PutChars2: 565 mov ah, HEX(0e) 566 mov bx, 7 567 int HEX(10) 568PutChars: 569 lodsb 570 or al, al 571 jnz short PutChars2 572 ret 573 574 575/****************************************************************************** 576 * Padding and boot sector signature * 577 ******************************************************************************/ 578 /* Pad to 509 bytes */ 579 .org 509 580 581BootPartition: 582 .byte 0 583 584BootSignature: 585 .word HEX(0aa55) // BootSector signature 586 587.endcode16 588 589END 590