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