[FREELDR]
[reactos.git] / reactos / boot / freeldr / bootsect / fat32.S
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Bootsector
4 * FILE:
5 * PURPOSE:
6 * PROGRAMMERS: ?
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
20 start:
21 jmp short main
22 nop
23
24 OEMName:
25 .ASCII "FrLdr1.0"
26 BytesPerSector:
27 .word 512
28 SectsPerCluster:
29 .byte 0
30 ReservedSectors:
31 .word 32
32 NumberOfFats:
33 .byte 2
34 MaxRootEntries:
35 .word 0 // Always zero for FAT32 volumes
36 TotalSectors:
37 .word 0 // Always zero for FAT32 volumes
38 MediaDescriptor:
39 .byte HEX(0f8)
40 SectorsPerFat:
41 .word 0 // Always zero for FAT32 volumes
42 SectorsPerTrack:
43 .word 0
44 NumberOfHeads:
45 .word 0
46 HiddenSectors:
47 .long 0
48 TotalSectorsBig:
49 .long 0
50
51 // FAT32 Inserted Info
52 SectorsPerFatBig:
53 .long 0
54 ExtendedFlags:
55 .word 0
56 FSVersion:
57 .word 0
58 RootDirStartCluster:
59 .long 0
60 FSInfoSector:
61 .word 0
62 BackupBootSector:
63 .word 6
64 Reserved1:
65 .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
66
67 // End FAT32 Inserted Info
68 BootDrive:
69 .byte 0
70 Reserved:
71 .byte 0
72 ExtendSig:
73 .byte HEX(29)
74 SerialNumber:
75 .long 0
76 VolumeLabel:
77 .ascii "NO NAME "
78 FileSystem:
79 .ascii "FAT32 "
80
81 main:
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
94 CheckSectorsPerFat:
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
98 CheckTotalSectors: // 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
101 CheckFileSystemVersion:
102 cmp word ptr BP_REL(FSVersion), 0 // Check the file system version word
103 jna GetDriveParameters // It is zero, so continue
104 CheckFailed:
105 jmp PrintFileSystemError // If it is not zero then exit with an error
106
107 GetDriveParameters:
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
119 CalcDriveSize:
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
139 LoadExtraBootCode:
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
155 ReadSectors:
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
164 CheckInt13hExtensions: // 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
177 ReadSectorsLBA:
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
184 ReadSectorsSetupDiskAddressPacket:
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
219 LBASectorsRead:
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
226 ReadSectorsCHS:
227 popad // Get logical sector number & sector count off stack
228
229 ReadSectorsCHSLoop:
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 ret
264
265 // Displays a disk error message
266 // And reboots
267 PrintDiskError:
268 mov si, offset msgDiskError // Bad boot disk message
269 call PutChars // Display it
270
271 jmp Reboot
272
273 // Displays a file system error message
274 // And reboots
275 PrintFileSystemError:
276 mov si, offset msgFileSystemError // FreeLdr not found message
277 call PutChars // Display it
278
279 Reboot:
280 mov si, offset msgAnyKey // Press any key message
281 call PutChars // Display it
282 xor ax, ax
283 int HEX(16) // Wait for a keypress
284 int HEX(19) // Reboot
285
286 PutChars:
287 lodsb
288 or al, al
289 jz short Done
290 mov ah, HEX(0e)
291 mov bx, 7
292 int HEX(10)
293 jmp short PutChars
294 Done:
295 ret
296
297
298 BiosCHSDriveSize:
299 .long 0
300
301 msgDiskError:
302 .ascii "Disk error", CR, LF, NUL
303 msgFileSystemError:
304 .ascii "File system error", CR, LF, NUL
305 msgAnyKey:
306 .ascii "Press any key to restart", CR, LF, NUL
307
308 .org 509 // Pad to 509 bytes
309
310 BootPartition:
311 .byte 0
312
313 BootSignature:
314 .word HEX(0aa55) // BootSector signature
315
316 // End of bootsector
317 //
318 // Now starts the extra boot code that we will store
319 // at sector 14 on a FAT32 volume
320 //
321 // To remain multi-boot compatible with other operating
322 // systems we must not overwrite anything other than
323 // the bootsector which means we will have to use
324 // a different sector like 14 to store our extra boot code
325
326
327
328 StartSearch:
329
330 // Now we must get the first cluster of the root directory
331 mov eax, dword ptr BP_REL(RootDirStartCluster)
332 cmp eax, HEX(0ffffff8) // Check to see if this is the last cluster in the chain
333 jb ContinueSearch // If not continue, if so then we didn't find freeldr.sys
334 jmp PrintFileNotFound
335
336 ContinueSearch:
337 mov bx, HEX(2000)
338 mov es, bx // Read cluster to [2000:0000h]
339 call ReadCluster // Read the cluster
340
341 // Now we have to find our way through the root directory to
342 // The OSLOADER.SYS file
343 xor bx,bx
344 mov bl, byte ptr BP_REL(SectsPerCluster)
345 shl bx, 4 // BX = BX * 512 / 32
346 mov ax, HEX(2000) // We loaded at 2000:0000
347 mov es, ax
348 xor di, di
349 mov si, offset filename
350 mov cx, 11
351 repe cmpsb // Compare filenames
352 jz FoundFile // If same we found it
353 dec bx
354 jnz FindFile
355 jmp PrintFileNotFound
356
357 FindFile:
358 mov ax, es // We didn't find it in the previous dir entry
359 add ax, 2 // So lets move to the next one
360 mov es, ax // And search again
361 xor di, di
362 mov si, offset filename
363 mov cx, 11
364 repe cmpsb // Compare filenames
365 jz FoundFile // If same we found it
366 dec bx // Keep searching till we run out of dir entries
367 jnz FindFile // Last entry?
368
369 // Get the next root dir cluster and try again until we run out of clusters
370 mov eax, dword ptr BP_REL(RootDirStartCluster)
371 call GetFatEntry
372 mov dword ptr BP_REL(RootDirStartCluster), eax
373 jmp StartSearch
374
375 FoundFile:
376 // Display "Loading FreeLoader..." message
377 mov si, offset msgLoading // Loading message
378 call PutChars // Display it
379
380 xor di, di // ES:DI has dir entry
381 xor dx, dx
382 mov ax, word ptr es:[di+20] // Get start cluster high word
383 shl eax, 16
384 mov ax, word ptr es:[di+26] // Get start cluster low word
385
386 CheckStartCluster:
387 cmp eax, 2 // Check and see if the start cluster starts at cluster 2 or above
388 jnb CheckEndCluster // If so then continue
389 jmp PrintFileSystemError // If not exit with error
390 CheckEndCluster:
391 cmp eax, HEX(0ffffff8) // Check and see if the start cluster is and end of cluster chain indicator
392 jb InitializeLoadSegment // If not then continue
393 jmp PrintFileSystemError // If so exit with error
394
395 InitializeLoadSegment:
396 mov bx, FREELDR_BASE / 16
397 mov es, bx
398
399 LoadFile:
400 cmp eax, HEX(0ffffff8) // Check to see if this is the last cluster in the chain
401 jae LoadFileDone // If so continue, if not then read the next one
402 push eax
403 xor bx, bx // Load ROSLDR starting at 0000:8000h
404 push es
405 call ReadCluster
406 pop es
407
408 xor bx, bx
409 mov bl, byte ptr BP_REL(SectsPerCluster)
410 shl bx, 5 // BX = BX * 512 / 16
411 mov ax, es // Increment the load address by
412 add ax, bx // The size of a cluster
413 mov es, ax
414
415 pop eax
416 push es
417 call GetFatEntry // Get the next entry
418 pop es
419
420 jmp LoadFile // Load the next cluster (if any)
421
422 LoadFileDone:
423 mov dl, byte ptr BP_REL(BootDrive) // Load boot drive into DL
424 mov dh, byte ptr ds:[BootPartition] // Load boot partition into DH
425
426 /* Transfer execution to the bootloader */
427 ljmp16 0, FREELDR_BASE
428
429 // Returns the FAT entry for a given cluster number
430 // On entry EAX has cluster number
431 // On return EAX has FAT entry for that cluster
432 GetFatEntry:
433
434 shl eax, 2 // EAX = EAX * 4 (since FAT32 entries are 4 bytes)
435 mov ecx, eax // Save this for later in ECX
436 xor edx, edx
437 movzx ebx, word ptr BP_REL(BytesPerSector)
438 push ebx
439 div ebx // FAT Sector Number = EAX / BytesPerSector
440 movzx ebx, word ptr BP_REL(ReservedSectors)
441 add eax, ebx // FAT Sector Number += ReservedSectors
442 mov ebx, dword ptr BP_REL(HiddenSectors)
443 add eax, ebx // FAT Sector Number += HiddenSectors
444 pop ebx
445 dec ebx
446 and ecx,ebx // FAT Offset Within Sector = ECX % BytesPerSector
447 // EAX holds logical FAT sector number
448 // ECX holds FAT entry offset
449
450 // Now we have to check the extended flags
451 // to see which FAT is the active one
452 // and use it, or if they are mirrored then
453 // no worries
454 movzx ebx, word ptr BP_REL(ExtendedFlags) // Get extended flags and put into ebx
455 and bx, HEX(0f) // Mask off upper 8 bits, now we have active fat in bl
456 jz LoadFatSector // If fat is mirrored then skip fat calcs
457 cmp bl, byte ptr BP_REL(NumberOfFats) // Compare bl to number of fats
458 jb GetActiveFatOffset
459 jmp PrintFileSystemError // If bl is bigger than numfats exit with error
460 GetActiveFatOffset:
461 push eax // Save logical FAT sector number
462 mov eax, dword ptr BP_REL(SectorsPerFatBig) // Get the number of sectors occupied by one fat in eax
463 mul ebx // Multiplied by the active FAT index we have in ebx
464 pop edx // Get logical FAT sector number
465 add eax, edx // Add the current FAT sector offset
466
467 LoadFatSector:
468 push ecx
469
470 mov bx, HEX(9000) // We will load it to [9000:0000h]
471 mov es, bx
472
473 // EAX holds logical FAT sector number
474 // Check if we have already loaded it
475 cmp eax, dword ptr ds:[FatSectorInCache]
476 je LoadFatSectorAlreadyLoaded
477
478 mov dword ptr ds:[FatSectorInCache], eax
479 xor bx, bx
480 mov cx, 1
481 call ReadSectors
482
483 LoadFatSectorAlreadyLoaded:
484 pop ecx
485 mov eax, dword ptr es:[ecx] // Get FAT entry
486 and eax, HEX(0fffffff) // Mask off reserved bits
487
488 ret
489
490 FatSectorInCache: // This variable tells us which sector we currently have in memory
491 .long HEX(0ffffffff) // There is no need to re-read the same sector if we don't have to
492
493
494 // Reads cluster number in EAX into [ES:0000]
495 ReadCluster:
496 // StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors//
497
498 dec eax
499 dec eax
500 xor edx, edx
501 movzx ebx, byte ptr BP_REL(SectsPerCluster)
502 mul ebx
503 push eax
504 xor edx, edx
505 movzx eax, byte ptr BP_REL(NumberOfFats)
506 mul dword ptr BP_REL(SectorsPerFatBig)
507 movzx ebx, word ptr BP_REL(ReservedSectors)
508 add eax, ebx
509 add eax, dword ptr BP_REL(HiddenSectors)
510 pop ebx
511 add eax, ebx // EAX now contains the logical sector number of the cluster
512 xor bx, bx // We will load it to [ES:0000], ES loaded before function call
513 movzx cx, byte ptr BP_REL(SectsPerCluster)
514 call ReadSectors
515 ret
516
517 // Displays a file not found error message
518 // And reboots
519 PrintFileNotFound:
520 mov si, offset msgFreeLdr // FreeLdr not found message
521 call PutChars // Display it
522 mov si, offset msgAnyKey // Press any key message
523 call PutChars // Display it
524
525 jmp Reboot
526
527 msgFreeLdr:
528 .ascii "freeldr.sys not found", CR, LF, NUL
529 filename:
530 .ascii "FREELDR SYS"
531 msgLoading:
532 .ascii "Loading FreeLoader...", CR, LF, NUL
533
534 .org 1022 // Pad to 1022 bytes
535
536 .word HEX(0aa55) // BootSector signature
537
538 .endcode16
539
540 END