Fix a couple of problems with FreeLDR portability.
[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 cmp eax,DWORD [BiosCHSDriveSize] ; Check if they are reading a sector outside CHS range
126 jae ReadSectorsLBA ; Yes - go to the LBA routine
127 ; If at all possible we want to use LBA routines because
128 ; They are optimized to read more than 1 sector per read
129
130 pushad ; Save logical sector number & sector count
131
132 CheckInt13hExtensions: ; Now check if this computer supports extended reads
133 mov ah,0x41 ; AH = 41h
134 mov bx,0x55aa ; BX = 55AAh
135 mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh)
136 int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK
137 jc ReadSectorsCHS ; CF set on error (extensions not supported)
138 cmp bx,0xaa55 ; BX = AA55h if installed
139 jne ReadSectorsCHS
140 test cl,1 ; CX = API subset support bitmap
141 jz ReadSectorsCHS ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
142
143 popad ; Restore sector count & logical sector number
144
145 ReadSectorsLBA:
146 pushad ; Save logical sector number & sector count
147
148 cmp cx,64 ; Since the LBA calls only support 0x7F sectors at a time we will limit ourselves to 64
149 jbe ReadSectorsSetupDiskAddressPacket ; If we are reading less than 65 sectors then just do the read
150 mov cx,64 ; Otherwise read only 64 sectors on this loop iteration
151
152 ReadSectorsSetupDiskAddressPacket:
153 mov [LBASectorsRead],cx
154 o32 push byte 0
155 push eax ; Put 64-bit logical block address on stack
156 push es ; Put transfer segment on stack
157 push bx ; Put transfer offset on stack
158 push cx ; Set transfer count
159 push byte 0x10 ; Set size of packet to 10h
160 mov si,sp ; Setup disk address packet on stack
161
162
163 mov dl,[BYTE bp+BootDrive] ; Drive number
164 mov ah,42h ; Int 13h, AH = 42h - Extended Read
165 int 13h ; Call BIOS
166 jc PrintDiskError ; If the read failed then abort
167
168 add sp,byte 0x10 ; Remove disk address packet from stack
169
170 popad ; Restore sector count & logical sector number
171
172 push bx
173 mov ebx,DWORD [LBASectorsRead]
174 add eax,ebx ; Increment sector to read
175 shl ebx,5
176 mov dx,es
177 add dx,bx ; Setup read buffer for next sector
178 mov es,dx
179 pop bx
180
181 sub cx,[LBASectorsRead]
182 jnz ReadSectorsLBA ; Read next sector
183
184 ret
185
186 LBASectorsRead:
187 dd 0
188
189
190 ; Reads logical sectors into [ES:BX]
191 ; EAX has logical sector number to read
192 ; CX has number of sectors to read
193 ReadSectorsCHS:
194 popad ; Get logical sector number & sector count off stack
195
196 ReadSectorsCHSLoop:
197 pushad
198 xor edx,edx
199 movzx ecx,WORD [BYTE bp+SectorsPerTrack]
200 div ecx ; Divide logical by SectorsPerTrack
201 inc dl ; Sectors numbering starts at 1 not 0
202 mov cl,dl ; Sector in CL
203 mov edx,eax
204 shr edx,16
205 div WORD [BYTE bp+NumberOfHeads] ; Divide logical by number of heads
206 mov dh,dl ; Head in DH
207 mov dl,[BYTE bp+BootDrive] ; Drive number in DL
208 mov ch,al ; Cylinder in CX
209 ror ah,1 ; Low 8 bits of cylinder in CH, high 2 bits
210 ror ah,1 ; in CL shifted to bits 6 & 7
211 or cl,ah ; Or with sector number
212 mov ax,0201h
213 int 13h ; DISK - READ SECTORS INTO MEMORY
214 ; AL = number of sectors to read, CH = track, CL = sector
215 ; DH = head, DL = drive, ES:BX -> buffer to fill
216 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
217
218 jc PrintDiskError ; If the read failed then abort
219
220 popad
221
222 inc eax ; Increment Sector to Read
223
224 mov dx,es
225 add dx,byte 20h ; Increment read buffer for next sector
226 mov es,dx
227
228 loop ReadSectorsCHSLoop ; Read next sector
229
230 ret
231
232
233
234
235 ; Displays a disk error message
236 ; And reboots
237 PrintDiskError:
238 mov si,msgDiskError ; Bad boot disk message
239 call PutChars ; Display it
240
241 jmp Reboot
242
243 ; Displays a file system error message
244 ; And reboots
245 PrintFileSystemError:
246 mov si,msgFileSystemError ; FreeLdr not found message
247 call PutChars ; Display it
248
249 Reboot:
250 mov si,msgAnyKey ; Press any key message
251 call PutChars ; Display it
252 xor ax,ax
253 int 16h ; Wait for a keypress
254 int 19h ; Reboot
255
256 PutChars:
257 lodsb
258 or al,al
259 jz short Done
260 mov ah,0eh
261 mov bx,07h
262 int 10h
263 jmp short PutChars
264 Done:
265 retn
266
267
268
269 BiosCHSDriveSize dd 0
270
271 msgDiskError db 'Disk error',0dh,0ah,0
272 msgFileSystemError db 'File system error',0dh,0ah,0
273 msgAnyKey db 'Press any key to restart',0dh,0ah,0
274
275 times 509-($-$$) db 0 ; Pad to 509 bytes
276
277 BootPartition:
278 db 0
279
280 BootSignature:
281 dw 0aa55h ; BootSector signature
282
283
284 ; End of bootsector
285 ;
286 ; Now starts the extra boot code that we will store
287 ; at sector 14 on a FAT32 volume
288 ;
289 ; To remain multi-boot compatible with other operating
290 ; systems we must not overwrite anything other than
291 ; the bootsector which means we will have to use
292 ; a different sector like 14 to store our extra boot code
293
294
295
296 StartSearch:
297 ; Now we must get the first cluster of the root directory
298 mov eax,DWORD [BYTE bp+RootDirStartCluster]
299 cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
300 jb ContinueSearch ; If not continue, if so then we didn't find freeldr.sys
301 jmp PrintFileNotFound
302 ContinueSearch:
303 mov bx,2000h
304 mov es,bx ; Read cluster to [2000:0000h]
305 call ReadCluster ; Read the cluster
306
307
308 ; Now we have to find our way through the root directory to
309 ; The OSLOADER.SYS file
310 xor bx,bx
311 mov bl,[BYTE bp+SectsPerCluster]
312 shl bx,4 ; BX = BX * 512 / 32
313 mov ax,2000h ; We loaded at 2000:0000
314 mov es,ax
315 xor di,di
316 mov si,filename
317 mov cx,11
318 rep cmpsb ; Compare filenames
319 jz FoundFile ; If same we found it
320 dec bx
321 jnz FindFile
322 jmp PrintFileNotFound
323
324 FindFile:
325 mov ax,es ; We didn't find it in the previous dir entry
326 add ax,2 ; So lets move to the next one
327 mov es,ax ; And search again
328 xor di,di
329 mov si,filename
330 mov cx,11
331 rep cmpsb ; Compare filenames
332 jz FoundFile ; If same we found it
333 dec bx ; Keep searching till we run out of dir entries
334 jnz FindFile ; Last entry?
335
336 ; Get the next root dir cluster and try again until we run out of clusters
337 mov eax,DWORD [BYTE bp+RootDirStartCluster]
338 call GetFatEntry
339 mov [BYTE bp+RootDirStartCluster],eax
340 jmp StartSearch
341
342 FoundFile:
343
344 ; Display "Loading FreeLoader..." message
345 mov si,msgLoading ; Loading message
346 call PutChars ; Display it
347
348 xor di,di ; ES:DI has dir entry
349 xor dx,dx
350 mov ax,WORD [es:di+14h] ; Get start cluster high word
351 shl eax,16
352 mov ax,WORD [es:di+1ah] ; Get start cluster low word
353
354 CheckStartCluster:
355 cmp eax,2 ; Check and see if the start cluster starts at cluster 2 or above
356 jnb CheckEndCluster ; If so then continue
357 jmp PrintFileSystemError ; If not exit with error
358 CheckEndCluster:
359 cmp eax,0ffffff8h ; Check and see if the start cluster is and end of cluster chain indicator
360 jb InitializeLoadSegment ; If not then continue
361 jmp PrintFileSystemError ; If so exit with error
362
363 InitializeLoadSegment:
364 mov bx,800h
365 mov es,bx
366
367 LoadFile:
368 cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
369 jae LoadFileDone ; If so continue, if not then read the next one
370 push eax
371 xor bx,bx ; Load ROSLDR starting at 0000:8000h
372 push es
373 call ReadCluster
374 pop es
375
376 xor bx,bx
377 mov bl,[BYTE bp+SectsPerCluster]
378 shl bx,5 ; BX = BX * 512 / 16
379 mov ax,es ; Increment the load address by
380 add ax,bx ; The size of a cluster
381 mov es,ax
382
383 pop eax
384 push es
385 call GetFatEntry ; Get the next entry
386 pop es
387
388 jmp LoadFile ; Load the next cluster (if any)
389
390 LoadFileDone:
391 mov dl,[BYTE bp+BootDrive] ; Load boot drive into DL
392 mov dh,[BootPartition] ; Load boot partition into DH
393 xor ax,ax
394 push ax ; We loaded at 0000:8000
395 push WORD 8000h ; We will do a far return to 0000:8000h
396 retf ; Transfer control to ROSLDR
397
398
399 ; Returns the FAT entry for a given cluster number
400 ; On entry EAX has cluster number
401 ; On return EAX has FAT entry for that cluster
402 GetFatEntry:
403
404 shl eax,2 ; EAX = EAX * 4 (since FAT32 entries are 4 bytes)
405 mov ecx,eax ; Save this for later in ECX
406 xor edx,edx
407 movzx ebx,WORD [BYTE bp+BytesPerSector]
408 push ebx
409 div ebx ; FAT Sector Number = EAX / BytesPerSector
410 movzx ebx,WORD [BYTE bp+ReservedSectors]
411 add eax,ebx ; FAT Sector Number += ReservedSectors
412 mov ebx,DWORD [BYTE bp+HiddenSectors]
413 add eax,ebx ; FAT Sector Number += HiddenSectors
414 pop ebx
415 dec ebx
416 and ecx,ebx ; FAT Offset Within Sector = ECX % BytesPerSector
417 ; EAX holds logical FAT sector number
418 ; ECX holds FAT entry offset
419
420 ; Now we have to check the extended flags
421 ; to see which FAT is the active one
422 ; and use it, or if they are mirrored then
423 ; no worries
424 movzx ebx,WORD [BYTE bp+ExtendedFlags] ; Get extended flags and put into ebx
425 and bx,0x0f ; Mask off upper 8 bits, now we have active fat in bl
426 jz LoadFatSector ; If fat is mirrored then skip fat calcs
427 cmp bl,[BYTE bp+NumberOfFats] ; Compare bl to number of fats
428 jb GetActiveFatOffset
429 jmp PrintFileSystemError ; If bl is bigger than numfats exit with error
430 GetActiveFatOffset:
431 push eax ; Save logical FAT sector number
432 mov eax,[BYTE bp+SectorsPerFatBig] ; Get the number of sectors occupied by one fat in eax
433 mul ebx ; Multiplied by the active FAT index we have in ebx
434 pop edx ; Get logical FAT sector number
435 add eax,edx ; Add the current FAT sector offset
436
437 LoadFatSector:
438 push ecx
439 ; EAX holds logical FAT sector number
440 ; Check if we have already loaded it
441 cmp eax,DWORD [FatSectorInCache]
442 je LoadFatSectorAlreadyLoaded
443
444 mov DWORD [FatSectorInCache],eax
445 mov bx,7000h
446 mov es,bx
447 xor bx,bx ; We will load it to [7000:0000h]
448 mov cx,1
449 call ReadSectors
450
451 LoadFatSectorAlreadyLoaded:
452 mov bx,7000h
453 mov es,bx
454 pop ecx
455 mov eax,DWORD [es:ecx] ; Get FAT entry
456 and eax,0fffffffh ; Mask off reserved bits
457
458 ret
459
460 FatSectorInCache: ; This variable tells us which sector we currently have in memory
461 dd 0ffffffffh ; There is no need to re-read the same sector if we don't have to
462
463
464 ; Reads cluster number in EAX into [ES:0000]
465 ReadCluster:
466 ; StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
467
468 dec eax
469 dec eax
470 xor edx,edx
471 movzx ebx,BYTE [BYTE bp+SectsPerCluster]
472 mul ebx
473 push eax
474 xor edx,edx
475 movzx eax,BYTE [BYTE bp+NumberOfFats]
476 mul DWORD [BYTE bp+SectorsPerFatBig]
477 movzx ebx,WORD [BYTE bp+ReservedSectors]
478 add eax,ebx
479 add eax,DWORD [BYTE bp+HiddenSectors]
480 pop ebx
481 add eax,ebx ; EAX now contains the logical sector number of the cluster
482 xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
483 movzx cx,BYTE [BYTE bp+SectsPerCluster]
484 call ReadSectors
485 ret
486
487
488 ; Displays a file not found error message
489 ; And reboots
490 PrintFileNotFound:
491 mov si,msgFreeLdr ; FreeLdr not found message
492 call PutChars ; Display it
493 mov si,msgAnyKey ; Press any key message
494 call PutChars ; Display it
495
496 jmp Reboot
497
498 msgFreeLdr db 'freeldr.sys not found',0dh,0ah,0
499 filename db 'FREELDR SYS'
500 msgLoading db 'Loading FreeLoader...',0dh,0ah,0
501
502
503 times 1022-($-$$) db 0 ; Pad to 1022 bytes
504
505 dw 0aa55h ; BootSector signature