xref: /reactos/boot/freeldr/bootsect/fat32.S (revision 53221834)
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