1/* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Bootsector 4 * FILE: boot/freeldr/bootsect/fat32.S 5 * PURPOSE: 6 * PROGRAMMERS: Brian Palmer 7 */ 8 9/* INCLUDES ******************************************************************/ 10 11#include <asm.inc> 12#include <freeldr/include/arch/pc/x86common.h> 13 14#define BP_REL(x) [bp+x-offset start] 15 16.code16 17 18//ORG HEX(7c00) 19 20start: 21 jmp short main 22 nop 23 24OEMName: 25 .ASCII "FrLdr1.0" 26BytesPerSector: 27 .word 512 28SectsPerCluster: 29 .byte 0 30ReservedSectors: 31 .word 32 32NumberOfFats: 33 .byte 2 34MaxRootEntries: 35 .word 0 // Always zero for FAT32 volumes 36TotalSectors: 37 .word 0 // Always zero for FAT32 volumes 38MediaDescriptor: 39 .byte HEX(0f8) 40SectorsPerFat: 41 .word 0 // Always zero for FAT32 volumes 42SectorsPerTrack: 43 .word 0 44NumberOfHeads: 45 .word 0 46HiddenSectors: 47 .long 0 48TotalSectorsBig: 49 .long 0 50 51// FAT32 Inserted Info 52SectorsPerFatBig: 53 .long 0 54ExtendedFlags: 55 .word 0 56FSVersion: 57 .word 0 58RootDirStartCluster: 59 .long 0 60FSInfoSector: 61 .word 0 62BackupBootSector: 63 .word 6 64Reserved1: 65 .space 12, 0 66// End FAT32 Inserted Info 67 68BootDrive: 69 .byte 0 70Reserved: 71 .byte 0 72ExtendSig: 73 .byte HEX(29) 74SerialNumber: 75 .long 0 76VolumeLabel: 77 .ascii "NO NAME " 78FileSystem: 79 .ascii "FAT32 " 80 81main: 82 xor ax,ax // Setup segment registers 83 mov ds,ax // Make DS correct 84 mov es,ax // Make ES correct 85 mov ss,ax // Make SS correct 86 mov bp, HEX(7c00) 87 mov sp, HEX(7c00) // Setup a stack 88 89 cmp byte ptr BP_REL(BootDrive), HEX(0ff) // If they have specified a boot drive then use it 90 jne CheckSectorsPerFat 91 92 mov byte ptr BP_REL(BootDrive), dl // Save the boot drive 93 94CheckSectorsPerFat: 95 96 cmp word ptr BP_REL(SectorsPerFat), 0 // Check the old 16-bit value of SectorsPerFat 97 jnz CheckFailed // If it is non-zero then exit with an error 98CheckTotalSectors: // Check the old 16-bit value of TotalSectors & MaxRootEntries 99 cmp dword ptr BP_REL(MaxRootEntries), 0 // by comparing the DWORD at offset MaxRootEntries to zero 100 jnz CheckFailed // If it is non-zero then exit with an error 101CheckFileSystemVersion: 102 cmp word ptr BP_REL(FSVersion), 0 // Check the file system version word 103 jna GetDriveParameters // It is zero, so continue 104CheckFailed: 105 jmp PrintFileSystemError // If it is not zero then exit with an error 106 107GetDriveParameters: 108 mov ax, HEX(0800) 109 mov dl, byte ptr BP_REL(BootDrive) // Get boot drive in dl 110 int HEX(13) // Request drive parameters from the bios 111 jnc CalcDriveSize // If the call succeeded then calculate the drive size 112 113 // If we get here then the call to the BIOS failed 114 // so just set CHS equal to the maximum addressable 115 // size 116 mov cx, HEX(0ffff) 117 mov dh, cl 118 119CalcDriveSize: 120 // Now that we have the drive geometry 121 // lets calculate the drive size 122 mov bl, ch // Put the low 8-bits of the cylinder count into BL 123 mov bh, cl // Put the high 2-bits in BH 124 shr bh, 6 // Shift them into position, now BX contains the cylinder count 125 and cl, HEX(3f) // Mask off cylinder bits from sector count 126 // CL now contains sectors per track and DH contains head count 127 movzx eax, dh // Move the heads into EAX 128 movzx ebx, bx // Move the cylinders into EBX 129 movzx ecx, cl // Move the sectors per track into ECX 130 inc eax // Make it one based because the bios returns it zero based 131 inc ebx // Make the cylinder count one based also 132 mul ecx // Multiply heads with the sectors per track, result in edx:eax 133 mul ebx // Multiply the cylinders with (heads * sectors) [stored in edx:eax already] 134 135 // We now have the total number of sectors as reported 136 // by the bios in eax, so store it in our variable 137 mov dword ptr ds:[BiosCHSDriveSize], eax 138 139LoadExtraBootCode: 140 // First we have to load our extra boot code at 141 // sector 14 into memory at [0000:7e00h] 142 mov eax, HEX(0e) 143 add eax, dword ptr BP_REL(HiddenSectors) // Add the number of hidden sectors 144 mov cx, 1 145 xor bx, bx 146 mov es, bx // Read sector to [0000:7e00h] 147 mov bx, HEX(7e00) 148 call ReadSectors 149 jmp StartSearch 150 151 152// Reads logical sectors into [ES:BX] 153// EAX has logical sector number to read 154// CX has number of sectors to read 155ReadSectors: 156 push es 157 cmp eax, dword ptr ds:[BiosCHSDriveSize] // Check if they are reading a sector outside CHS range 158 jae ReadSectorsLBA // Yes - go to the LBA routine 159 // If at all possible we want to use LBA routines because 160 // They are optimized to read more than 1 sector per read 161 162 pushad // Save logical sector number & sector count 163 164CheckInt13hExtensions: // Now check if this computer supports extended reads 165 mov ah, HEX(41) // AH = 41h 166 mov bx, HEX(55aa) // BX = 55AAh 167 mov dl, byte ptr BP_REL(BootDrive) // DL = drive (80h-FFh) 168 int HEX(13) // IBM/MS INT 13 Extensions - INSTALLATION CHECK 169 jc ReadSectorsCHS // CF set on error (extensions not supported) 170 cmp bx, HEX(0aa55) // BX = AA55h if installed 171 jne ReadSectorsCHS 172 test cl,1 // CX = API subset support bitmap 173 jz ReadSectorsCHS // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported 174 175 popad // Restore sector count & logical sector number 176 177ReadSectorsLBA: 178 pushad // Save logical sector number & sector count 179 180 cmp cx, 64 // Since the LBA calls only support 0x7F sectors at a time we will limit ourselves to 64 181 jbe ReadSectorsSetupDiskAddressPacket // If we are reading less than 65 sectors then just do the read 182 mov cx, 64 // Otherwise read only 64 sectors on this loop iteration 183 184ReadSectorsSetupDiskAddressPacket: 185 mov word ptr ds:[LBASectorsRead],cx 186 push 0 187 push 0 188 push eax // Put 64-bit logical block address on stack 189 push es // Put transfer segment on stack 190 push bx // Put transfer offset on stack 191 push cx // Set transfer count 192 push 16 // Set size of packet to 10h 193 mov si, sp // Setup disk address packet on stack 194 195 mov dl, byte ptr BP_REL(BootDrive) // Drive number 196 mov ah, HEX(42) // Int 13h, AH = 42h - Extended Read 197 int HEX(13) // Call BIOS 198 jc PrintDiskError // If the read failed then abort 199 200 add sp, 16 // Remove disk address packet from stack 201 202 popad // Restore sector count & logical sector number 203 204 push bx 205 mov ebx, dword ptr ds:[LBASectorsRead] 206 add eax, ebx // Increment sector to read 207 shl ebx, 5 208 mov dx, es 209 add dx, bx // Setup read buffer for next sector 210 mov es, dx 211 pop bx 212 213 sub cx, word ptr ds:[LBASectorsRead] 214 jnz ReadSectorsLBA // Read next sector 215 216 pop es 217 ret 218 219LBASectorsRead: 220 .long 0 221 222 223// Reads logical sectors into [ES:BX] 224// EAX has logical sector number to read 225// CX has number of sectors to read 226ReadSectorsCHS: 227 popad // Get logical sector number & sector count off stack 228 229ReadSectorsCHSLoop: 230 pushad 231 xor edx, edx 232 movzx ecx, word ptr BP_REL(SectorsPerTrack) 233 div ecx // Divide logical by SectorsPerTrack 234 inc dl // Sectors numbering starts at 1 not 0 235 mov cl, dl // Sector in CL 236 mov edx, eax 237 shr edx, 16 238 div word ptr BP_REL(NumberOfHeads) // Divide logical by number of heads 239 mov dh, dl // Head in DH 240 mov dl, byte ptr BP_REL(BootDrive) // Drive number in DL 241 mov ch, al // Cylinder in CX 242 ror ah, 1 // Low 8 bits of cylinder in CH, high 2 bits 243 ror ah, 1 // in CL shifted to bits 6 & 7 244 or cl, ah // Or with sector number 245 mov ax, HEX(0201) 246 int HEX(13) // DISK - READ SECTORS INTO MEMORY 247 // AL = number of sectors to read, CH = track, CL = sector 248 // DH = head, DL = drive, ES:BX -> buffer to fill 249 // Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read 250 251 jc PrintDiskError // If the read failed then abort 252 253 popad 254 255 inc eax // Increment Sector to Read 256 257 mov dx, es 258 add dx, 32 // Increment read buffer for next sector 259 mov es, dx 260 261 loop ReadSectorsCHSLoop // Read next sector 262 263 pop es 264 ret 265 266// Displays a disk error message 267// And reboots 268PrintDiskError: 269 mov si, offset msgDiskError // Bad boot disk message 270 call PutChars // Display it 271 272 jmp Reboot 273 274// Displays a file system error message 275// And reboots 276PrintFileSystemError: 277 mov si, offset msgFileSystemError // FreeLdr not found message 278 call PutChars // Display it 279 280Reboot: 281 mov si, offset msgAnyKey // Press any key message 282 call PutChars // Display it 283 xor ax, ax 284 int HEX(16) // Wait for a keypress 285 int HEX(19) // Reboot 286 287PutChars: 288 lodsb 289 or al, al 290 jz short Done 291 mov ah, HEX(0e) 292 mov bx, 7 293 int HEX(10) 294 jmp short PutChars 295Done: 296 ret 297 298 299BiosCHSDriveSize: 300 .long 0 301 302msgDiskError: 303 .ascii "Disk error", CR, LF, NUL 304msgFileSystemError: 305 .ascii "File system error", CR, LF, NUL 306msgAnyKey: 307 .ascii "Press any key to restart", CR, LF, NUL 308 309.org 509 // Pad to 509 bytes 310 311BootPartition: 312 .byte 0 313 314BootSignature: 315 .word HEX(0aa55) // BootSector signature 316 317// End of bootsector 318// 319// Now starts the extra boot code that we will store 320// at sector 14 on a FAT32 volume 321// 322// To remain multi-boot compatible with other operating 323// systems we must not overwrite anything other than 324// the bootsector which means we will have to use 325// a different sector like 14 to store our extra boot code 326 327 328 329StartSearch: 330 331 // Now we must get the first cluster of the root directory 332 mov eax, dword ptr BP_REL(RootDirStartCluster) 333 cmp eax, HEX(0ffffff8) // Check to see if this is the last cluster in the chain 334 jb ContinueSearch // If not continue, if so then we didn't find freeldr.sys 335 jmp PrintFileNotFound 336 337ContinueSearch: 338 mov bx, HEX(2000) 339 mov es, bx // Read cluster to [2000:0000h] 340 call ReadCluster // Read the cluster 341 342 // Now we have to find our way through the root directory to 343 // The FREELDR.SYS file 344 xor bx,bx 345 mov bl, byte ptr BP_REL(SectsPerCluster) 346 shl bx, 4 // BX = BX * 512 / 32 347 mov ax, HEX(2000) // We loaded at 2000:0000 348 mov es, ax 349 xor di, di 350 mov si, offset filename 351 mov cx, 11 352 repe cmpsb // Compare filenames 353 jz FoundFile // If same we found it 354 dec bx 355 jnz FindFile 356 jmp PrintFileNotFound 357 358FindFile: 359 mov ax, es // We didn't find it in the previous dir entry 360 add ax, 2 // So lets move to the next one 361 mov es, ax // And search again 362 xor di, di 363 mov si, offset filename 364 mov cx, 11 365 repe cmpsb // Compare filenames 366 jz FoundFile // If same we found it 367 dec bx // Keep searching till we run out of dir entries 368 jnz FindFile // Last entry? 369 370 // Get the next root dir cluster and try again until we run out of clusters 371 mov eax, dword ptr BP_REL(RootDirStartCluster) 372 call GetFatEntry 373 mov dword ptr BP_REL(RootDirStartCluster), eax 374 jmp StartSearch 375 376FoundFile: 377 // Display "Loading FreeLoader..." message 378 mov si, offset msgLoading // Loading message 379 call PutChars // Display it 380 381 xor di, di // ES:DI has dir entry 382 xor dx, dx 383 mov ax, word ptr es:[di+20] // Get start cluster high word 384 shl eax, 16 385 mov ax, word ptr es:[di+26] // Get start cluster low word 386 387CheckStartCluster: 388 cmp eax, 2 // Check and see if the start cluster starts at cluster 2 or above 389 jnb CheckEndCluster // If so then continue 390 jmp PrintFileSystemError // If not exit with error 391CheckEndCluster: 392 cmp eax, HEX(0ffffff8) // Check and see if the start cluster is and end of cluster chain indicator 393 jb InitializeLoadSegment // If not then continue 394 jmp PrintFileSystemError // If so exit with error 395 396InitializeLoadSegment: 397 mov bx, FREELDR_BASE / 16 398 mov es, bx 399 400LoadFile: 401 cmp eax, HEX(0ffffff8) // Check to see if this is the last cluster in the chain 402 jae LoadFileDone // If so continue, if not then read the next one 403 push eax 404 xor bx, bx // Load ROSLDR starting at 0000:F800h 405 push es 406 call ReadCluster 407 pop es 408 409 xor bx, bx 410 mov bl, byte ptr BP_REL(SectsPerCluster) 411 shl bx, 5 // BX = BX * 512 / 16 412 mov ax, es // Increment the load address by 413 add ax, bx // The size of a cluster 414 mov es, ax 415 416 pop eax 417 push es 418 call GetFatEntry // Get the next entry 419 pop es 420 421 jmp LoadFile // Load the next cluster (if any) 422 423LoadFileDone: 424 mov dl, byte ptr BP_REL(BootDrive) // Load boot drive into DL 425 mov dh, byte ptr ds:[BootPartition] // Load boot partition into DH 426 427 /* Transfer execution to the bootloader */ 428 ljmp16 0, FREELDR_BASE 429 430// Returns the FAT entry for a given cluster number 431// On entry EAX has cluster number 432// On return EAX has FAT entry for that cluster 433GetFatEntry: 434 435 shl eax, 2 // EAX = EAX * 4 (since FAT32 entries are 4 bytes) 436 mov ecx, eax // Save this for later in ECX 437 xor edx, edx 438 movzx ebx, word ptr BP_REL(BytesPerSector) 439 push ebx 440 div ebx // FAT Sector Number = EAX / BytesPerSector 441 movzx ebx, word ptr BP_REL(ReservedSectors) 442 add eax, ebx // FAT Sector Number += ReservedSectors 443 mov ebx, dword ptr BP_REL(HiddenSectors) 444 add eax, ebx // FAT Sector Number += HiddenSectors 445 pop ebx 446 dec ebx 447 and ecx,ebx // FAT Offset Within Sector = ECX % BytesPerSector 448 // EAX holds logical FAT sector number 449 // ECX holds FAT entry offset 450 451 // Now we have to check the extended flags 452 // to see which FAT is the active one 453 // and use it, or if they are mirrored then 454 // no worries 455 movzx ebx, word ptr BP_REL(ExtendedFlags) // Get extended flags and put into ebx 456 and bx, HEX(0f) // Mask off upper 8 bits, now we have active fat in bl 457 jz LoadFatSector // If fat is mirrored then skip fat calcs 458 cmp bl, byte ptr BP_REL(NumberOfFats) // Compare bl to number of fats 459 jb GetActiveFatOffset 460 jmp PrintFileSystemError // If bl is bigger than numfats exit with error 461GetActiveFatOffset: 462 push eax // Save logical FAT sector number 463 mov eax, dword ptr BP_REL(SectorsPerFatBig) // Get the number of sectors occupied by one fat in eax 464 mul ebx // Multiplied by the active FAT index we have in ebx 465 pop edx // Get logical FAT sector number 466 add eax, edx // Add the current FAT sector offset 467 468LoadFatSector: 469 push ecx 470 471 mov bx, HEX(9000) // We will load it to [9000:0000h] 472 mov es, bx 473 474 // EAX holds logical FAT sector number 475 // Check if we have already loaded it 476 cmp eax, dword ptr ds:[FatSectorInCache] 477 je LoadFatSectorAlreadyLoaded 478 479 mov dword ptr ds:[FatSectorInCache], eax 480 xor bx, bx 481 mov cx, 1 482 call ReadSectors 483 484LoadFatSectorAlreadyLoaded: 485 pop ecx 486 mov eax, dword ptr es:[ecx] // Get FAT entry 487 and eax, HEX(0fffffff) // Mask off reserved bits 488 489 ret 490 491FatSectorInCache: // This variable tells us which sector we currently have in memory 492 .long HEX(0ffffffff) // There is no need to re-read the same sector if we don't have to 493 494 495// Reads cluster number in EAX into [ES:0000] 496ReadCluster: 497 // StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors; 498 499 dec eax 500 dec eax 501 xor edx, edx 502 movzx ebx, byte ptr BP_REL(SectsPerCluster) 503 mul ebx 504 push eax 505 xor edx, edx 506 movzx eax, byte ptr BP_REL(NumberOfFats) 507 mul dword ptr BP_REL(SectorsPerFatBig) 508 movzx ebx, word ptr BP_REL(ReservedSectors) 509 add eax, ebx 510 add eax, dword ptr BP_REL(HiddenSectors) 511 pop ebx 512 add eax, ebx // EAX now contains the logical sector number of the cluster 513 xor bx, bx // We will load it to [ES:0000], ES loaded before function call 514 movzx cx, byte ptr BP_REL(SectsPerCluster) 515 call ReadSectors 516 ret 517 518// Displays a file not found error message 519// And reboots 520PrintFileNotFound: 521 mov si, offset msgFreeLdr // FreeLdr not found message 522 call PutChars // Display it 523 524 jmp Reboot 525 526msgFreeLdr: 527 .ascii "freeldr.sys not found", CR, LF, NUL 528filename: 529 .ascii "FREELDR SYS" 530msgLoading: 531 .ascii "Loading FreeLoader...", CR, LF, NUL 532 533.org 1022 // Pad to 1022 bytes 534 535 .word HEX(0aa55) // BootSector signature 536 537.endcode16 538 539END 540