[FATX]
authorTimo Kreuzer <timo.kreuzer@reactos.org>
Mon, 17 Sep 2012 21:44:55 +0000 (21:44 +0000)
committerTimo Kreuzer <timo.kreuzer@reactos.org>
Mon, 17 Sep 2012 21:44:55 +0000 (21:44 +0000)
Rewrite FAT bootsector. It's written to eventually replace FAT12, FAT16 and FAT32 bootsectors using the same code. Currently only used for FAT16. The complete (!) code to load freeldr has been put into a single 512 byte sector. CHS support is not implemented atm, but there's plenty space left (41 bytes). Fixes boot on harddisks smaller than 513 MB.
CORE-6610 #resolve

svn path=/trunk/; revision=57319

reactos/boot/freeldr/bootsect/CMakeLists.txt
reactos/boot/freeldr/bootsect/fatx.S [new file with mode: 0644]

index dc60f67..51b0ebb 100644 (file)
@@ -4,7 +4,7 @@ if(ARCH STREQUAL "i386" OR ARCH STREQUAL "amd64")
 CreateBootSectorTarget(dosmbr ${CMAKE_CURRENT_SOURCE_DIR}/dosmbr.S ${CMAKE_CURRENT_BINARY_DIR}/dosmbr.bin 7c00)
 CreateBootSectorTarget(ext2 ${CMAKE_CURRENT_SOURCE_DIR}/ext2.S ${CMAKE_CURRENT_BINARY_DIR}/ext2.bin 0)
 CreateBootSectorTarget(fat32 ${CMAKE_CURRENT_SOURCE_DIR}/fat32.S ${CMAKE_CURRENT_BINARY_DIR}/fat32.bin 7c00)
-CreateBootSectorTarget(fat ${CMAKE_CURRENT_SOURCE_DIR}/fat.S ${CMAKE_CURRENT_BINARY_DIR}/fat.bin 7c00)
+CreateBootSectorTarget(fat ${CMAKE_CURRENT_SOURCE_DIR}/fatx.S ${CMAKE_CURRENT_BINARY_DIR}/fat.bin 7c00)
 CreateBootSectorTarget(isoboot ${CMAKE_CURRENT_SOURCE_DIR}/isoboot.S ${CMAKE_CURRENT_BINARY_DIR}/isoboot.bin 7000)
 CreateBootSectorTarget(isobtrt ${CMAKE_CURRENT_SOURCE_DIR}/isobtrt.S ${CMAKE_CURRENT_BINARY_DIR}/isobtrt.bin 7000)
 
diff --git a/reactos/boot/freeldr/bootsect/fatx.S b/reactos/boot/freeldr/bootsect/fatx.S
new file mode 100644 (file)
index 0000000..8702eca
--- /dev/null
@@ -0,0 +1,587 @@
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS Bootsector
+ * FILE:            fatx.S
+ * PURPOSE:         Combined FAT16 and FAT32 boot sector
+ * PROGRAMMERS:     Brian Palmer
+ *                  Timo Kreuzer
+ */
+
+
+/*
+ * Layout of a FAT volume:
+ *
+ * |---------------------------------------------------------
+ * | * BootSector                         |
+ * | * FS Information Sector (FAT32 only) | ReservedSectors
+ * | * ... more reserved sectors ...      |
+ * |--------------------------------------------------------
+ * | * FAT 1                              | NumberOfFats
+ * | * FAT 2                              |      *
+ * | * [more FATs]                        | SectorsPerFat
+ * |---------------------------------------------------------
+ * | * Root Directory (FAT12/FAT16 only)  | MaxRootEntries / 16
+ * |---------------------------------------------------------
+ * | * File data                          |
+ * | ....                                 |
+ * |----------------------------------------
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <asm.inc>
+#include "../freeldr/include/arch/pc/x86common.h"
+
+#define ADDRESS_FOR_DIRENTRIES HEX(10000)
+
+SizeOfDataArea        = 32
+
+/* Put the stack below the data area */
+BootSectorStackTop    = (HEX(7c00) - SizeOfDataArea)
+
+/* Data area offsets for uninitialized data */
+DataAreaStart         = BootSectorStackTop + 0  /* dword */
+#ifndef FAT32
+RootDirStartSector    = BootSectorStackTop + 4  /* dword */
+#endif
+BiosCHSDriveSize      = BootSectorStackTop + 8  /* dword */
+LBASectorsRead        = BootSectorStackTop + 12 /* dword */
+ReadSectorsOffset     = BootSectorStackTop + 16 /* word */
+ReadClusterOffset     = BootSectorStackTop + 18 /* word */
+PutCharsOffset        = BootSectorStackTop + 20 /* word */
+
+/* Macro for bp relative memory access to reduce code size */
+#define BP_REL(x) ss:[bp + x - BootSectorStackTop]
+
+/* The code starts at 0x7c00 */
+// org 7c00h
+
+.code16
+
+/******************************************************************************
+ *                      BIOS Parameter Block (BPB)                            *
+ ******************************************************************************/
+/* We have 3 bytes at the entry point to jump over the data area */
+start:
+    jmp short main
+    nop
+
+/* Here starts the BIOS Parameter Block (BPB) data.
+   The real data will be copied during install */
+OEMName:
+    .ascii "FrLdr1.0"
+BytesPerSector:
+    .word 512
+SectorsPerCluster:
+    .byte 0
+ReservedSectors:
+    .word 32
+NumberOfFats:
+    .byte 2
+MaxRootEntries:
+    .word 0            // Always zero for FAT32 volumes
+TotalSectors:
+    .word 0            // Always zero for FAT32 volumes
+MediaDescriptor:
+    .byte HEX(0f8)
+SectorsPerFat:
+    .word 0            // Always zero for FAT32 volumes
+SectorsPerTrack:
+    .word 0
+NumberOfHeads:
+    .word 0
+HiddenSectors:
+    .long 0
+TotalSectorsBig:
+    .long 0
+
+/* Extra data for FAT32 volumes */
+#ifdef FAT32
+SectorsPerFatBig:
+    .long    0
+ExtendedFlags:
+    .word    0
+FSVersion:
+    .word    0
+RootDirStartCluster:
+    .long    0
+FSInfoSector:
+    .word    0
+BackupBootSector:
+    .word    6
+Reserved1:
+    .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+#endif // FAT32
+
+BootDrive:
+    .byte 0
+Reserved:
+    .byte 0
+ExtendSig:
+    .byte HEX(29)
+SerialNumber:
+    .long 0
+VolumeLabel:
+    .ascii "NO NAME    "
+FileSystem:
+    .ascii "FATxx   "
+
+
+/******************************************************************************
+ *                             String data                                    *
+ ******************************************************************************/
+
+filename:
+    .ascii "FREELDR SYS"
+
+msgBootFailure:
+    .ascii "Load failed!", CR, LF, NUL
+
+msgAnyKey:
+    .ascii "Press any key to reboot...", NUL
+
+
+/******************************************************************************
+ *                           Main code entry                                  *
+ * Input: DL = Boot drive                                                     *
+ ******************************************************************************/
+main:
+    /* First setup the segment registers */
+    xor ax, ax
+    mov ds, ax
+    mov ss, ax
+
+    /* Load the stack pointer */
+    mov sp, BootSectorStackTop
+
+    /* Load bp for relative memory access, which saves us some bytes of code
+       size, when used with 32 bit instructions */
+    mov bp, sp
+
+    /* Load the boot drive from the BPB into al */
+    mov al, byte ptr ds:[BootDrive]
+
+    /* Check if it's valid */
+    cmp al, HEX(0ff)
+    je .SaveBootDrive
+
+    /* Copy it into dl */
+    mov dl, al
+
+.SaveBootDrive:
+    /* Save the bootdrive in the BPB */
+    mov byte ptr ds:[BootDrive], dl
+
+
+/******************************************************************************
+ *                        Get drive parameters                                *
+ ******************************************************************************/
+
+    /* Call INT 13 to get the drive parameters:
+       AH = 08h
+       DL = drive (bit 7 set for hard disk)
+       ES:DI = 0000h:0000h to guard against BIOS bugs */
+    xor di, di
+    mov ah, 8
+    int HEX(13)
+
+    /* Return from INT 13h/08h:
+        CF set on error -> AH = status (07h)
+        CF clear if successful -> AH = 00h
+        AL = 00h on at least some BIOSes
+        BL = drive type (AT/PS2 floppies only)
+        CH = low eight bits of maximum cylinder number
+        CL = bits 0:5 maximum sector number, bits 7:8 high two bits of maximum cylinder number
+        DH = maximum head number
+        DL = number of drives
+        ES:DI -> drive parameter table (floppies only) */
+
+    /* Check for failure */
+    jc BootFailure
+
+
+/******************************************************************************
+ *                         Calculate drive size                               *
+ ******************************************************************************/
+
+    movzx ebx, ch           // Put the low 8-bits of the cylinder count into EBX
+    mov bh, cl              // Put the high 2-bits in BH
+    shr bh, 6               // Shift them into position, now BX contains the cylinder count
+
+    and cl, HEX(3f)         // Mask off cylinder bits from sector count
+    movzx ecx, cl           // Move the sectors per track into ECX
+
+    movzx eax, dh           // Move the heads into EAX
+
+    inc eax                 // Make it one based because the bios returns it zero based
+    inc ebx                 // Make the cylinder count one based also
+    mul ecx                 // Multiply heads with the sectors per track, result in edx:eax
+    mul ebx                 // Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
+
+    // We now have the total number of sectors as reported
+    // by the bios in eax, so store it in our variable
+    mov dword ptr BP_REL(BiosCHSDriveSize), eax
+
+
+/******************************************************************************
+ *                             Load the FAT                                   *
+ ******************************************************************************/
+
+    /* Load the number of first sector of the FAT into eax */
+    movzx eax, word ptr BP_REL(ReservedSectors)
+    add eax, dword ptr BP_REL(HiddenSectors)
+
+    /* Load sector count into ecx */
+#if FAT32
+    mov ecx, BP_REL(SectorsPerFatBig)
+#else
+    movzx ecx, word ptr BP_REL(SectorsPerFat)
+#endif
+
+    /* Save FAT sector and size for later use */
+    pushad
+
+    /* Point ES:DI to the memory that is later the disk read buffer for freeldr.
+       This way we cannot overwrite our FAT with freeldr data */
+    mov bx, DISKREADBUFFER / 16
+    mov es,bx
+    xor di, di
+
+    /* Read the sectors */
+    call ReadSectors
+
+    /* Restore FAT sector and size */
+    popad
+
+
+/******************************************************************************
+ *                 Get root directory / data area start                       *
+ ******************************************************************************/
+
+    /* Copy reserved + hidden sectors to EBX */
+    mov ebx, eax
+
+    /* Calculate (NumberOfFats * SectorsPerFat) */
+    movzx eax, byte ptr BP_REL(NumberOfFats)
+    mul ecx
+
+    /* Add reserved sectors and hidden sectors */
+    add eax, ebx
+
+#ifndef FAT32
+    /* Save the starting sector of the root directory */
+    mov dword ptr BP_REL(RootDirStartSector), eax
+
+    /* Calculate number of sectors for the root dir:
+       sectors = MaxRootEntries * 32 / 512 (rounded up!) */
+    movzx ebx, word ptr BP_REL(MaxRootEntries)
+    add ebx, 15
+    shr ebx, 4
+
+    /* Add the root dir start sector and save it as DataAreaStart */
+    add ebx, eax
+    mov dword ptr BP_REL(DataAreaStart), ebx
+#else
+    mov dword ptr BP_REL(DataAreaStart), eax
+
+    /* On FAT32 volumes the root dir start cluster is stored in the BPB */
+    mov eax, dword ptr BP_REL(RootDirStartCluster)
+#endif
+
+
+/******************************************************************************
+ *                 Search the root directory for freeldr                      *
+ ******************************************************************************/
+.SearchForFreeldr:
+
+    /* Load ES with the segment where we put the dir entries */
+    mov bx, ADDRESS_FOR_DIRENTRIES / 16
+    mov es, bx
+
+    /* Set the address offset to 0 */
+    xor di, di
+
+#ifdef FAT32
+    /* Read the dir cluster. This loads the next cluster into EAX */
+    call ReadCluster
+
+    /* Calculate the numer of dir entries in this cluster:
+       dx = SectorsPerCluster * 512 / 32 */
+    movzx dx, byte ptr ds:[SectorsPerCluster]
+    shl dx, 4
+#else
+    /* Set the number of sectors to read to 1 */
+    xor cx, cx
+    inc cx
+
+    /* Read the sector, but preserve ES */
+    push es
+    call ReadSectors
+    pop es
+
+    /* Set entry count to entries per sector */
+    mov dx, (512 / 32)
+#endif
+
+    /* Load the start offset of the dir entries into ebx */
+    xor bx, bx
+
+.CheckDirEntry:
+    /* Load the address of the name into di */
+    mov di, bx
+
+    /* If the first byte of the entry is 0 then we have reached the end */
+    cmp byte ptr es:[di], ch
+    jz BootFailure
+
+    /* Compare with freeldr file name */
+    mov si, offset filename
+    mov cx, 11
+    repe cmpsb
+
+    /* Check if we found the file */
+    jz .FoundFreeLoader
+
+    /* File didn't match, go to next entry */
+    add bx, 32
+
+    /* Decrement entry count and check if we reached the end */
+    dec dx
+    jnz .CheckDirEntry
+
+#if FAT32
+    /* Check to see if this was the last cluster in the chain */
+    cmp eax, HEX(0ffffff8)
+    jnb BootFailure
+#endif
+
+    /* Repeat the search process with the next sector / cluster.
+       eax is already incremented in ReadSectors / ReadCluster */
+    jmp .SearchForFreeldr
+
+
+/******************************************************************************
+ *                            Load freeldr                                    *
+ ******************************************************************************/
+.FoundFreeLoader:
+
+    /* Load the cluster number of freeldr into eax */
+#if FAT32
+#error unsupported
+#else
+    movzx eax, word ptr es:[bx + HEX(1A)]
+#endif
+
+    /* Load es:di with the freeldr start address */
+    mov dx, FREELDR_BASE / 16
+    mov es, dx
+    xor di, di
+
+.LoadNextCluster:
+    /* Load the cluster to the current address. EAX is adjusted to the next
+       cluster and ES is adjusted for the next read */
+    call ReadCluster
+
+    /* Check if this is the last cluster in the chain */
+#if FAT32
+    cmp eax, HEX(0ffffff8)
+#elif FAT12
+    cmp ax, HEX(0ff8)
+#else
+    cmp ax, HEX(0fff8)
+#endif
+    jb .LoadNextCluster
+
+    /* Load boot drive into DL, boot partition into DH */
+    mov  dl, byte ptr ds:[BootDrive]
+    mov  dh, byte ptr ds:[BootPartition]
+
+    /* Now the complete freeldr imag is loaded.
+       Jump to the realmode entry point. */
+    ljmp16 0, FREELDR_BASE
+
+
+
+BootFailure:
+    mov  si, offset msgBootFailure
+    call PutChars
+
+
+Reboot:
+    /* Output "Press any key to reboot" message */
+    mov  si, offset msgAnyKey
+    call PutChars
+
+    /* Wait for a keypress */
+    xor  ax, ax
+    int  HEX(16)
+
+    /* Reboot */
+    int  HEX(19)
+
+
+/******************************************************************************
+ * PROCEDURE ReadCluster                                                      *
+ * Input: EAX = Cluster number, ES:DI = Target                                *
+ * Modifies: EAX (next cluster number), BX, DX (undefined)                    *
+ ******************************************************************************/
+ReadCluster:
+
+    pushad
+
+    // StartSector = ((Cluster - 2) * SectorsPerCluster) + SectorsForFat + ReservedSectors + HiddenSectors
+    // StartSector = ((Cluster - 2) * SectorsPerCluster) + DataAreaStart
+
+    /* Substract 2 */
+    dec eax
+    dec eax
+
+    /* Multiply with SectorsPerCluster */
+    movzx ecx, byte ptr BP_REL(SectorsPerCluster)
+    mul ecx
+
+    /* Add DataAreaStart */
+    add eax, dword ptr BP_REL(DataAreaStart)
+
+    /* Call ReadSectors. EAX = SectorNumber, ECX = SectorsPerCluster */
+    call ReadSectors
+
+    /* Restore the cluster number */
+    popad
+
+    /* Save ES */
+    push es
+
+#if FAT32
+#error FAT23 not implemented
+#elif FAT12
+#error FAT12 not implemented
+#else
+    /* DX:AX = AX * 2 (since FAT16 entries are 2 bytes) */
+    mov bx, 2
+    mul bx
+
+    /* Shift DX, so that it is the segment offset: DX = DX * (64K / 16) */
+    shl dx, 12
+
+    /* Put segment address of FAT into ES */
+    add dx, DISKREADBUFFER / 16
+    mov es, dx
+
+    /* Put the FAT entry offset into EBX for indirect mov */
+    mov bx, ax
+
+    /* Put the content of the FAT entry into AX */
+    mov ax, es:[bx]
+#endif
+
+    /* Restore ES and return */
+    pop  es
+    ret
+
+
+/******************************************************************************
+ * PROCEDURE ReadSectors                                                      *
+ * Input: EAX = Sector start number, ECX = number of sectors, ES:DI = Target  *
+ * Modifies: EAX (incremented by sector count), CX = 0, ES (incremented),     *
+ *           EBX undefined                                                    *
+ ******************************************************************************/
+ReadSectors:
+    /* We could possibly also implement CHS, but it's currently unimplemented */
+//jmp $
+ReadSectorsLBA:
+
+    /* Copy number of sectors to ebx */
+    movzx ebx, cx
+
+    /* Since the LBA calls only support 0x7F sectors at a time,
+       we will limit ourselves to 64 */
+    cmp bx, 64
+    jbe .ReadSectorsLBA2
+    mov bx, 64
+
+.ReadSectorsLBA2:
+
+    /* Save logical sector number & sector count */
+    pushad
+
+    /* Setup the disk address packet on the stack */
+    .byte HEX(66) // size overwrite prefix for next push
+    push 0    // Put 64-bit logical block address (high part) on stack
+    push eax  // Put 64-bit logical block address (low part) on stack
+    push es   // Put transfer segment on stack
+    push di   // Put transfer offset on stack
+    push bx   // Set transfer count (for this round)
+    push 16   // Set size of packet to 16
+
+    /* Point si to the disk address packet on stack */
+    mov si, sp
+
+    /* Set the drive number */
+    mov dl, byte ptr ds:[BootDrive]
+//jmp $
+    /* Call INT 13h, AH = 42h - Extended Read
+       Input: ...
+       Modifies: ... */
+    mov ah, HEX(42)
+    int HEX(13)
+//jmp $
+    /* Check for failure */
+    jc BootFailure
+
+    /* Remove disk address packet from stack */
+    add sp, 16
+
+    /* Adjust ES to point to the next sector */
+    shl bx, 5
+    mov ax, es
+    add ax, bx
+    mov es, ax
+
+    /* Restore sector count & logical sector number */
+    popad
+
+    /* Adjust the sector number to the next sector we need to read
+       by adding the number of sectors that we read */
+    add eax, ebx
+
+    /* Adjust remaining sectors */
+    sub cx, bx
+    jnz ReadSectorsLBA
+
+    /* return */
+    ret
+
+
+
+/******************************************************************************
+ * PROCEDURE PutChars                                                         *
+ * Input: ESI = Points to string to be printed                                *
+ * Modifies: AL, AH, SI                                                       *
+ ******************************************************************************/
+PutChars2:
+    mov ah, HEX(0e)
+    mov bx, 7
+    int HEX(10)
+PutChars:
+    lodsb
+    or al, al
+    jnz short PutChars2
+    ret
+
+
+/******************************************************************************
+ *               Padding and boot sector signature                            *
+ ******************************************************************************/
+    /* Pad to 509 bytes */
+ //   .org 509
+
+BootPartition:
+    .byte 0
+
+BootSignature:
+    .word HEX(0aa55)   // BootSector signature
+
+.endcode16
+
+END