2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Bootsector
4 * FILE: boot/freeldr/bootsect/faty.S
5 * PURPOSE: Combined FAT12, FAT16 and FAT32 boot sector
6 * PROGRAMMERS: Brian Palmer
10 #define DISKREADBUFFER HEX(8E000)
13 * Layout of a FAT volume:
15 * |---------------------------------------------------------
17 * | * FS Information Sector (FAT32 only) | ReservedSectors
18 * | * ... more reserved sectors ... |
19 * |--------------------------------------------------------
20 * | * FAT 1 | NumberOfFats
22 * | * [more FATs] | SectorsPerFat
23 * |---------------------------------------------------------
24 * | * Root Directory (FAT12/FAT16 only) | MaxRootEntries / 16
25 * |---------------------------------------------------------
28 * |----------------------------------------
31 /* INCLUDES ******************************************************************/
34 #include <freeldr/include/arch/pc/x86common.h>
36 #define ADDRESS_FOR_DIRENTRIES HEX(10000)
40 /* Put the stack below the data area */
41 BootSectorStackTop = (HEX(7c00) - SizeOfDataArea)
43 /* Data area offsets for uninitialized data */
44 DataAreaStart = BootSectorStackTop + 0 /* dword */
46 RootDirStartSector = BootSectorStackTop + 4 /* dword */
48 BiosCHSDriveSize = BootSectorStackTop + 8 /* dword */
49 LBASectorsRead = BootSectorStackTop + 12 /* dword */
50 ReadSectorsOffset = BootSectorStackTop + 16 /* word */
51 ReadClusterOffset = BootSectorStackTop + 18 /* word */
52 PutCharsOffset = BootSectorStackTop + 20 /* word */
54 /* Macro for bp relative memory access to reduce code size */
55 #define BP_REL(x) ss:[bp + x - BootSectorStackTop]
57 /* The code starts at 0x7c00 */
62 /******************************************************************************
63 * BIOS Parameter Block (BPB) *
64 ******************************************************************************/
65 /* We have 3 bytes at the entry point to jump over the data area */
67 jmp short main // FIXME: When compiling FAT32, assembler will complain
68 // that the label is too far... Need investigation!
71 /* Here starts the BIOS Parameter Block (BPB) data.
72 The real data will be copied during install */
84 .word 0 // Always zero for FAT32 volumes
86 .word 0 // Always zero for FAT32 volumes
90 .word 0 // Always zero for FAT32 volumes
100 /* Extra data for FAT32 volumes */
132 /******************************************************************************
134 ******************************************************************************/
140 .ascii "Load failed!", CR, LF, NUL
143 .ascii "Press any key to reboot...", NUL
146 /******************************************************************************
148 * Input: DL = Boot drive *
149 ******************************************************************************/
151 /* First setup the segment registers */
156 /* Load the stack pointer */
157 mov sp, BootSectorStackTop
159 /* Load bp for relative memory access, which saves us some bytes of code
160 size, when used with 32 bit instructions */
163 /* Load the boot drive from the BPB into al */
164 mov al, byte ptr ds:[BootDrive]
166 /* Check if it's valid */
170 /* Copy it into dl */
174 /* Save the bootdrive in the BPB */
175 mov byte ptr ds:[BootDrive], dl
178 /******************************************************************************
179 * Get drive parameters *
180 ******************************************************************************/
182 /* Call INT 13 to get the drive parameters:
184 DL = drive (bit 7 set for hard disk)
185 ES:DI = 0000h:0000h to guard against BIOS bugs */
190 /* Return from INT 13h/08h:
191 CF set on error -> AH = status (07h)
192 CF clear if successful -> AH = 00h
193 AL = 00h on at least some BIOSes
194 BL = drive type (AT/PS2 floppies only)
195 CH = low eight bits of maximum cylinder number
196 CL = bits 0:5 maximum sector number, bits 7:8 high two bits of maximum cylinder number
197 DH = maximum head number
198 DL = number of drives
199 ES:DI -> drive parameter table (floppies only) */
201 /* Check for failure */
205 /******************************************************************************
206 * Calculate drive size *
207 ******************************************************************************/
209 movzx ebx, ch // Put the low 8-bits of the cylinder count into EBX
210 mov bh, cl // Put the high 2-bits in BH
211 shr bh, 6 // Shift them into position, now BX contains the cylinder count
213 and cl, HEX(3f) // Mask off cylinder bits from sector count
214 movzx ecx, cl // Move the sectors per track into ECX
216 movzx eax, dh // Move the heads into EAX
218 inc eax // Make it one based because the bios returns it zero based
219 inc ebx // Make the cylinder count one based also
220 mul ecx // Multiply heads with the sectors per track, result in edx:eax
221 mul ebx // Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
223 // We now have the total number of sectors as reported
224 // by the bios in eax, so store it in our variable
225 mov dword ptr BP_REL(BiosCHSDriveSize), eax
228 /******************************************************************************
230 ******************************************************************************/
232 /* Load the number of first sector of the FAT into eax */
233 movzx eax, word ptr BP_REL(ReservedSectors)
234 add eax, dword ptr BP_REL(HiddenSectors)
236 /* Load sector count into ecx */
238 mov ecx, dword ptr BP_REL(SectorsPerFatBig)
240 movzx ecx, word ptr BP_REL(SectorsPerFat)
243 /* Save FAT sector and size for later use */
246 /* Point ES:DI to the memory that is later the disk read buffer for freeldr.
247 This way we cannot overwrite our FAT with freeldr data */
248 mov bx, DISKREADBUFFER / 16
252 /* Read the sectors */
255 /* Restore FAT sector and size */
259 /******************************************************************************
260 * Get root directory / data area start *
261 ******************************************************************************/
263 /* Copy reserved + hidden sectors to EBX */
266 /* Calculate (NumberOfFats * SectorsPerFat) */
267 movzx eax, byte ptr BP_REL(NumberOfFats)
270 /* Add reserved sectors and hidden sectors */
274 /* Save the starting sector of the root directory */
275 mov dword ptr BP_REL(RootDirStartSector), eax
277 /* Calculate number of sectors for the root dir:
278 sectors = MaxRootEntries * 32 / 512 (rounded up!) */
279 movzx ebx, word ptr BP_REL(MaxRootEntries)
283 /* Add the root dir start sector and save it as DataAreaStart */
285 mov dword ptr BP_REL(DataAreaStart), ebx
287 mov dword ptr BP_REL(DataAreaStart), eax
289 /* On FAT32 volumes the root dir start cluster is stored in the BPB */
290 mov eax, dword ptr BP_REL(RootDirStartCluster)
294 /******************************************************************************
295 * Search the root directory for freeldr *
296 ******************************************************************************/
299 /* Load ES with the segment where we put the dir entries */
300 mov bx, ADDRESS_FOR_DIRENTRIES / 16
303 /* Set the address offset to 0 */
307 /* Read the dir cluster. This loads the next cluster into EAX */
310 /* Calculate the numer of dir entries in this cluster:
311 dx = SectorsPerCluster * 512 / 32 */
312 movzx dx, byte ptr ds:[SectorsPerCluster]
315 /* Set the number of sectors to read to 1 */
319 /* Read the sector, but preserve ES */
324 /* Set entry count to entries per sector */
328 /* Load the start offset of the dir entries into ebx */
332 /* Load the address of the name into di */
335 /* If the first byte of the entry is 0 then we have reached the end */
336 cmp byte ptr es:[di], ch
339 /* Compare with freeldr file name */
340 mov si, offset filename
344 /* Check if we found the file */
347 /* File didn't match, go to next entry */
350 /* Decrement entry count and check if we reached the end */
355 /* Check to see if this was the last cluster in the chain */
356 cmp eax, HEX(0ffffff8)
360 /* Repeat the search process with the next sector / cluster.
361 eax is already incremented in ReadSectors / ReadCluster */
362 jmp .SearchForFreeldr
365 /******************************************************************************
367 ******************************************************************************/
370 /* Load the cluster number of freeldr into eax */
374 movzx eax, word ptr es:[bx + HEX(1A)]
377 /* Load es:di with the freeldr start address */
378 mov dx, FREELDR_BASE / 16
383 /* Load the cluster to the current address. EAX is adjusted to the next
384 cluster and ES is adjusted for the next read */
387 /* Check if this is the last cluster in the chain */
389 cmp eax, HEX(0ffffff8)
397 /* Load boot drive into DL, boot partition into DH */
398 mov dl, byte ptr ds:[BootDrive]
399 mov dh, byte ptr ds:[BootPartition]
401 /* Now the complete freeldr imag is loaded.
402 Jump to the realmode entry point. */
403 ljmp16 0, FREELDR_BASE
408 mov si, offset msgBootFailure
413 /* Output "Press any key to reboot" message */
414 mov si, offset msgAnyKey
417 /* Wait for a keypress */
425 /******************************************************************************
426 * PROCEDURE ReadCluster *
427 * Input: EAX = Cluster number, ES:DI = Target *
428 * Modifies: EAX (next cluster number), BX, DX (undefined) *
429 ******************************************************************************/
434 // StartSector = ((Cluster - 2) * SectorsPerCluster) + SectorsForFat + ReservedSectors + HiddenSectors
435 // StartSector = ((Cluster - 2) * SectorsPerCluster) + DataAreaStart
441 /* Multiply with SectorsPerCluster */
442 movzx ecx, byte ptr BP_REL(SectorsPerCluster)
445 /* Add DataAreaStart */
446 add eax, dword ptr BP_REL(DataAreaStart)
448 /* Call ReadSectors. EAX = SectorNumber, ECX = SectorsPerCluster */
451 /* Restore the cluster number */
458 #error FAT32 not implemented
460 #error FAT12 not implemented
462 /* DX:AX = AX * 2 (since FAT16 entries are 2 bytes) */
466 /* Shift DX, so that it is the segment offset: DX = DX * (64K / 16) */
469 /* Put segment address of FAT into ES */
470 add dx, DISKREADBUFFER / 16
473 /* Put the FAT entry offset into EBX for indirect mov */
476 /* Put the content of the FAT entry into AX */
480 /* Restore ES and return */
485 /******************************************************************************
486 * PROCEDURE ReadSectors *
487 * Input: EAX = Sector start number, ECX = number of sectors, ES:DI = Target *
488 * Modifies: EAX (incremented by sector count), CX = 0, ES (incremented), *
490 ******************************************************************************/
492 /* We could possibly also implement CHS, but it's currently unimplemented */
496 /* Copy number of sectors to ebx */
499 /* Since the LBA calls only support 0x7F sectors at a time,
500 we will limit ourselves to 64 */
507 /* Save logical sector number & sector count */
510 /* Setup the disk address packet on the stack */
511 .byte HEX(66) // size overwrite prefix for next push
512 push 0 // Put 64-bit logical block address (high part) on stack
513 push eax // Put 64-bit logical block address (low part) on stack
514 push es // Put transfer segment on stack
515 push di // Put transfer offset on stack
516 push bx // Set transfer count (for this round)
517 push 16 // Set size of packet to 16
519 /* Point si to the disk address packet on stack */
522 /* Set the drive number */
523 mov dl, byte ptr ds:[BootDrive]
525 /* Call INT 13h, AH = 42h - Extended Read
531 /* Check for failure */
534 /* Remove disk address packet from stack */
537 /* Adjust ES to point to the next sector */
543 /* Restore sector count & logical sector number */
546 /* Adjust the sector number to the next sector we need to read
547 by adding the number of sectors that we read */
550 /* Adjust remaining sectors */
559 /******************************************************************************
560 * PROCEDURE PutChars *
561 * Input: ESI = Points to string to be printed *
562 * Modifies: AL, AH, SI *
563 ******************************************************************************/
575 /******************************************************************************
576 * Padding and boot sector signature *
577 ******************************************************************************/
578 /* Pad to 509 bytes */
585 .word HEX(0aa55) // BootSector signature