mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
589 lines
17 KiB
ArmAsm
589 lines
17 KiB
ArmAsm
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Bootsector
|
|
* FILE: boot/freeldr/bootsect/faty.S
|
|
* PURPOSE: Combined FAT12, FAT16 and FAT32 boot sector
|
|
* PROGRAMMERS: Brian Palmer
|
|
* Timo Kreuzer
|
|
*/
|
|
|
|
#define DISKREADBUFFER HEX(8E000)
|
|
|
|
/*
|
|
* 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 // FIXME: When compiling FAT32, assembler will complain
|
|
// that the label is too far... Need investigation!
|
|
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:
|
|
.space 12, 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 */
|
|
#ifdef FAT32
|
|
mov ecx, dword ptr 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
|
|
|
|
#ifdef 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 */
|
|
#ifdef 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 defined(FAT32)
|
|
cmp eax, HEX(0ffffff8)
|
|
#elif defined(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 defined(FAT32)
|
|
#error FAT32 not implemented
|
|
#elif defined(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
|