CloverBootloader/CloverEFI/BootSector/BootDuet.S
2019-09-03 12:58:42 +03:00

942 lines
25 KiB
ArmAsm

/*
* BootDuet - Replacement boot program for DUET.
* Copyright 2011 Miguel Lopes Santos Ramos <mail@miguel.ramos.name>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* See:
* - README for a description of this project
* - INSTALL for installation instructions
*/
/*
* The Master Boot Record (MBR) will have loaded this program into physical
* address 0x7c00 and jumped into 0000:7c00 just as the BIOS would.
* The environment that EFILDR expects us to have prepared is as follows:
*
* 0x015000 - 0x019000 EFIVAR.BIN loaded here, if the file exists
* 0x019000 - 0x019004 The serial number of the FAT boot volume
* 0x019004 - 0x019005 One byte indicating the following conditions:
* - 0 - EFIVAR.BIN was loaded
* - 1 - EFIVAR.BIN does not exist
* - 2 - EFIVAR.BIN exists but is not exactly 16k
* 0x020000 - 0x0a0000 EFILDR loaded here
*
* Given those constraints, we define our memory map as follows. Note that we
* work mostly in the lower 64k of physical memory with segment registers set
* to zero.
*
* 0x006000 - 0x007000 Room for several FAT sectors (4k) (FAT12)
* 0x007000 - 0x007c00 We have 3k here for stack and local variables
* 0x007c00 - 0x007e00 Where BootDuet is loaded (512b)
* 0x007e00 - 0x008000 Room for one FAT sector (FAT32 and FAT16)
* 0x008000 - 0x010000 Room for root directory entries (32k at most)
* 0x010000 - 0x015000 Unused space (20k)
* 0x015000 - 0x019000 Room for EFIVAR.BIN (16k at most)
* 0x019000 - 0x019005 Parameters for EFILDR (5 bytes)
* 0x019005 - 0x020000 Unused space (almost 4k)
* 0x020000 - 0x0a0000 Room for EFILDR (512k at most)
*/
.equ BaseAddress, 0x7c00 # Offset address for our own code and data
#if FAT == 32 || FAT == 16
.equ FatOffset, 0x7e00 # Offset address for the FAT buffer
#else
.equ FatOffset, 0x6000 # Offset address for the FAT buffer
#endif
.equ RootOffset, 0x8000 # Offset address for the root dir buffer
/*
* Constants everyone knows about.
*/
.equ DirEntrySize, 32 # Size of directory entry for all types of FAT
.equ DirEntryShift, 5 # Shift factor for directory entry
#if defined(WITH_LBA_64BIT)
.equ SizeOfLBA, 8 # Bytes in a 64-bit LBA variable
#else
.equ SizeOfLBA, 4 # Bytes in a 32-bit LBA variable
#endif
#if FAT == 32
.equ SizeOfCluster, 4 # Bytes in a cluster pointer variable
#elif FAT == 16 || FAT == 12
.equ SizeOfCluster, 2 # Bytes in a cluster pointer variable
#endif
/*
* This is a 16-bit code segment and we're in real mode.
*/
.code16
/*
* This jump is canonical, don't change the target.
*/
.global _start
_start: jmp main
nop
/*
* BIOS Parameter Block (BPB)
*
* The BPB is the same for all types of FAT filesystems.
* The following labels are placeholders that make writing the code that
* accesses this data easier.
* The real data is what is filled in by the format program.
* The intention is that you only overwrite the part of your volume boot
* sector that contains the code and leave this part unchanged.
*/
.space 8, 0x20 # OEM ID waste (OS or format program)
.word 512 # Bytes per sector (will anything other than 512 work?)
.byte 0 # Sectors per cluster
.word 0 # Reserved sectors before first FAT including boot
.byte 0 # Number of FATs (usually 1 or 2)
.word 0 # Root directory entries (only FAT12 or FAT16)
.word 0 # Number of sectors or 0 (not used)
.byte 0 # Media descriptor (irrelevant)
.word 0 # Sectors per FAT (for FAT12 or FAT16)
.word 0 # Sectors per track (irrelevant for LBA)
.word 0 # Number of heads (irrelevant for LBA)
.long 0 # Hidden sectors before partition
.long 0 # Number of sectors (if wSectors = 0)
/*
* The next symbols allow us to reference exactly that same data in the BPB,
* but relatively to our main stack frame.
* Addressing relative to the frame pointer saves us a byte on each memory
* reference, however, GNU as makes it hard for us to do it.
*/
.equ main_wBps, 0x0b
.equ main_bSpc, 0x0d
.equ main_wReserved, 0x0e
.equ main_bFats, 0x10
.equ main_wRootEntries, 0x11
.equ main_wSectsPerFat, 0x16
.equ main_lHidden, 0x1c
#if FAT == 32
/*
* Extended BIOS Parameter Block (EBPB) for FAT32.
*/
.long 0 # Sectors per FAT (for FAT32)
.word 0 # FAT flags (irrelevant)
.word 0 # FAT32 version (only known is 0)
.long 0 # First cluster of the root directory
.word 0 # Sector number of FSINFO (irrelevant)
.word 0 # Backup boot sector number or 0 for no backup
.space 12, 0 # another 12 bytes gone to waste
bDrive: .byte 0 # BIOS drive number (we use what comes in DL)
.byte 0 # another byte gone to waste
.byte 0 # EBPB signature (should be 0x29)
.long 0 # Volume ID (serial number)
.space 11, 0x20# Volume label (8.3) (irrelevant)
.space 8, 0x20 # another 8 bytes gone to waste ("FAT32 ")
/*
* As before, symbols for memory references relative to the main stack frame.
*/
.equ main_lSectsPerFat, 0x24
.equ main_lRootCluster, 0x2c
.equ main_bDrive, 0x40
.equ main_bSignature, 0x42
.equ main_lVolId, 0x43
.equ main_FsType, 0x52
#elif FAT == 16 || FAT == 12
/*
* Extended BIOS Parameter Block (EBPB) for recent versions of FAT12 and FAT16.
*/
bDrive: .byte 0 # BIOS drive number (we use what comes in DL)
.byte 0 # another byte gone to waste
.byte 0 # EBPB signature (should be 0x29)
.long 0 # Volume ID (serial number)
.space 11, 0x20# Volume label (8.3) (irrelevant)
.space 8, 0x20 # File system type ("FAT12 " or "FAT16 ")
/*
* As before, symbols for memory references relative to the main stack frame.
*/
.equ main_bDrive, 0x24
.equ main_bSignature, 0x26
.equ main_lVolId, 0x27
.equ main_FsType, 0x36
#endif
/*
* And we finally start our main function.
* We have 420 bytes left for the boot code, those wasted bytes would have
* come in handy.
* We'll probably have DL set with the BIOS boot disk number. If this is an MBR
* disk, we may even have SI pointing to the right partition entry, if it is GPT
* maybe a boot loader faked a partition entry. Anyway, we don't trust that.
*/
main:
/*
* Initialize our memory model with all segment registers at 0000.
*/
cli # no stack, no interrupts
pushw %cs
popw %ss # SS = CS = 0000
movw $BaseAddress,%sp # stack goes down from 7c00
sti # got stack, got interrupts
cld # we work upward
pushw %cs
popw %ds # DS = CS = 0000
movw %sp,%bp # yes, we use a frame pointer
#if !defined(WITH_HARDCODED_DRIVE)
movb %dl,main_bDrive(%bp) # store drive number, which the
# MBR should have passed us on
# DL, to the EBPB.
#endif
#if defined(WITH_VALIDATION) && !defined(DEBUG)
/*
* fsck - boot sector validation. This validation is only to help troubleshoot.
* On tighter versions, such as FAT12 with LBA 64 bit, we skip this, there's no
* space.
*/
fsck:
/*
* - check that file system signature is 0x29
* - check that hidden sectors isn't zero
*/
cmpb $0x29,main_bSignature(%bp)
jne fsck.1
cmpl $0,main_lHidden(%bp)
jne fsck.2
/*
* Something went wrong with the installation, tell the user and halt.
*/
fsck.1:
movw $Invalid,%si # SI = error message
movb $InvalidLen,%cl # CL = length
call print
fsck.h: jmp fsck.h # Hot halt
fsck.2:
/*
* Fall through to fsinit.
*/
#endif
/*
* init - initializes variables on the main stack frame.
*
* returns:
* sFat, sRoot, wRootSects and sData variables are set,
* wRootEnd, bEfiLdr, bEfiVar and cFatCache are set to harmless defaults.
*
* registers trashed: EAX, EBX, ECX, EDX
*/
init:
/*
* The following assembler symbol is a neat trick that helps us not
* getting lost when computing frame offsets of local variables.
*/
main_frame = 0
/*
* Compute the LBA of the first sector of the first FAT.
*/
main_frame = main_frame - SizeOfLBA
main_sFat = main_frame # LBA at which FAT starts
#if !defined(WITH_LBA_64BIT)
movzwl main_wReserved(%bp),%eax
addl main_lHidden(%bp),%eax # EAX = hidden + reserved
pushl %eax # store to sFat
#else
movzwl main_wReserved(%bp),%eax
xorl %edx,%edx
addl main_lHidden(%bp),%eax
adcl lHiddenHigh,%edx # EDX:EAX = hidden + reserved
pushl %edx # store to sFat
pushl %eax
#endif
/*
* Compute the LBA of the first sector after all copies of FAT. This
* will be, on FAT12 and FAT16, the first sector of the root directory
* and on FAT32 the first sector of the first cluster (the data region).
*/
#if FAT == 32
main_frame = main_frame - SizeOfLBA
main_sData = main_frame # LBA at which data starts
movl main_lSectsPerFat(%bp),%ecx
#elif FAT == 16 || FAT == 12
main_frame = main_frame - SizeOfLBA
main_sRoot = main_frame # LBA at which root starts
movzwl main_wSectsPerFat(%bp),%ecx
#endif
movzbl main_bFats(%bp),%eax # EAX = number of FATs
mull %ecx # EDX:EAX = total FAT sectors
#if !defined(WITH_LBA_64BIT)
addl main_sFat(%bp),%eax # EAX = sFat + total FAT
pushl %eax # store to sData/sRoot
#else
addl main_sFat(%bp),%eax
adcl main_sFat+4(%bp),%edx # EDX:EAX = sFat + total FAT
pushl %edx # store to sData/sRoot
pushl %eax
#endif
#if FAT == 12 || FAT == 16
/*
* Compute the LBA of the first sector of the root directory, which
* comes immediately after all copies of the FAT.
*/
main_frame = main_frame - 2
main_wRootSects = main_frame # number of sectors in root
main_frame = main_frame - SizeOfLBA
main_sData = main_frame # LBA at which data starts
movzwl main_wRootEntries(%bp),%eax
bsrw main_wBps(%bp),%cx
subw $DirEntryShift,%cx
shrw %cl,%ax
pushw %ax # store to wRootSects
#if !defined(WITH_LBA_64BIT)
addl main_sRoot(%bp),%eax # EAX = sRoot + wRootSects
pushl %eax # store to sData
#else
xorl %edx,%edx
addl main_sRoot(%bp),%eax
adcl main_sRoot+4(%bp),%edx # EDX:EAX = sRoot + wRootSects
pushl %edx # store to sData
pushl %eax
#endif
#endif
/*
* Other variables that must be initialized.
* cEfiVar must come lower in memory than cEfiLdr. Together, they form
* a sorted array of two elements.
*/
main_frame = main_frame - 2
main_wRootEnd = main_frame # ptr to end off root buffer
main_frame = main_frame - 1
main_bEfiLdr = main_frame # EFILDR present?
main_frame = main_frame - 1
main_bEfiVar = main_frame # EFIVAR.BIN present?
main_frame = main_frame - SizeOfCluster
main_cFatCache = main_frame # loaded/cached FAT sector
pushw %cs # zero wRootEnd
pushw $1 # bEfiLdr = 0, bEfiVar = 1
#if FAT == 32
pushw %cs # set cFatCache to funny value
pushw $0xffff
#elif FAT == 16 || FAT == 12
pushw $0xffff # set cFatCache to funny value
#endif
/*
* Fall through to readroot.
*/
/*
* readroot - reads the root directory into the predetermined buffer.
*
* returns:
* wRootEnd variable is set
*
* registers trashed: EAX, EBX, ECX, EDX, SI
*/
readroot:
/*
* We read the root directory to a predifined buffer and hope that it
* won't cross the boundary of the first 64k.
*/
pushw %cs
popw %es # ES = CS = 0000
movw $RootOffset,%di # ES:DI = root buffer
#if FAT == 32
/*
* On FAT32, root is a normal file with the start cluster registered on
* the EBPB.
*/
movl main_lRootCluster(%bp),%eax
call fread # read the root directory
movw %di,main_wRootEnd(%bp) # store DI to wRootEnd
#elif FAT == 16 || FAT == 12
/*
* On FAT16 and FAT12, we must compute the number of sectors taken by
* the root directory and read them from the disk.
*/
movw main_wRootSects(%bp),%cx
movl main_sRoot(%bp),%eax # EDX:EAX = start LBA of root
#if !defined(WITH_LBA_64BIT)
xorl %edx,%edx
#else
movl main_sRoot+4(%bp),%edx # EDX:EAX = start LBA of root
#endif
/*
* Read all sectors on the root directory.
*/
call read
/*
* After reading, advance DI by the number of bytes read and return.
* We don't advance ES. Even if we did, the next routine would have
* a problem if the root directory was superimposed by the files
* that it reads.
*/
movw main_wBps(%bp),%ax
mulw %cx # DX:AX = bytes read
addw %ax,%di # advance DI by AX bytes
movw %di,main_wRootEnd(%bp) # store DI to wRootEnd
#endif
/*
* Fall through to scanroot.
*/
/*
* scanroot - scans the root directory for EFILDR and EFIVAR.BIN, if any of
* these files is found, it is read and bEfiLdr or bEfiVar variables are
* set appropriately
*
* returns:
* bEfiLdr and bEfiVar variables are set
*
* registers trashed: EAX, EBX, ECX, EDX, SI, DI, ES
*/
scanroot:
movw $RootOffset,%si # DS:SI = root buffer
scanroot.1:
/*
* Check if this directory entry is EFILDR
*/
pushw %cs
popw %es # ES = CS
movw $EfiLdr,%di # ES:DI = &EfiLdr
call fncmp
jne scanroot.2 # jump if this isn't EFILDR
movb $1,main_bEfiLdr(%bp) # bEfiLdr = 1, we have it
pushw $0x2000 # read EFILDR to 2000:0000
jmp scanroot.r
scanroot.2:
/*
* Check if this directory entry is EFIVAR.BIN and, if so, check its
* size to see if it is 16k and then set the bHaveVar variable
* accordingly.
* If the size is right, then proceed to reading it.
*/
movw $EfiVar,%di # ES:DI = &EfiVar (assume ES=CS)
call fncmp
jne scanroot.n # jump if this isn't EFIVAR.BIN
movb $2,main_bEfiVar(%bp) # bEfiVar = 2, maybe size is bad
#if FAT == 12
cmpl $0,0x1c(%si) # is zero size?
je scanroot.n # it is, skip it
#else
cmpl $0x4000,0x1c(%si) # compare size to 16k
jne scanroot.n # size is not exactly 16k...
#endif
movb $0,main_bEfiVar(%bp) # bEfiVar = 0, we have it
pushw $0x1500 # read EFIVAR.BIN to 1500:0000
scanroot.r:
/*
* Found interesting file, print its name, so the user knows something
* if we halt for some reason, and read it to ES:0000, where ES is the
* word on top of the stack.
*/
movb $FilenameLen,%cl # CL = number of chars
call print # print the file name
popw %es # restore ES from the stack
xorw %di,%di # ES:DI = xxxx:0000
#if FAT == 32
pushw 0x14(%si) # push high 16 bits of cluster
pushw 0x1a(%si) # push low 16 bits of cluster
popl %eax # EAX = 32-bit cluster number
#elif FAT == 16 || FAT == 12
movw 0x1a(%si),%ax # AX = 16-bit cluster number
#endif
call fread # read file EAX into ES:DI
scanroot.n:
/*
* Proceed to the next directory entry.
*/
addw $DirEntrySize,%si # advance SI
cmpw main_wRootEnd(%bp),%si # have we reached the end?
jb scanroot.1 # if not, loop
/*
* Fall through to setup.
*/
/*
* setup - sets up environment to what EFILDR expects to find
*/
setup:
/*
* Check that EFILDR was found and that we have something to jump into.
*/
movw main_bEfiVar(%bp),%ax # AL = bEfiVar, AH = bEfiLdr
or %ah,%ah # is bEfiLdr set?
jnz setup.1
/*
* Print error message and halt.
*/
movw $Missing,%si # SI = Missing
movb $MissingLen,%cl # CL = length
call print
setup.h: jmp setup.h # Hot halt
setup.1:
#if !defined(DEBUG)
/*
* Copy the volume id (serial number) of the FAT file system to the
* physical address 19000 and the value of bEfiVar to 19004.
*/
pushw $0x1900
popw %es # ES = 1900
movb %al,%es:(4) # store AL to 1900:0004
movl main_lVolId(%bp),%eax # EAX = volume serial number
movl %eax,%es:(0) # store EAX to 1900:0000
/*
* Jump into EFILDR at 2000:0200, the second sector of start.com.
*/
ljmp $0x2000,$0x0200
#endif
#if defined(DEBUG)
/*
* printn - print a 32-bit number on the screen in decimal and halt
*
* parameters:
* EAX number to print
*/
printn:
leaw main_FsType(%bp),%si # Overwrite volume label and id
xorl %ebx,%ebx
movb $10,%bl # EBX = decimal base
xorw %cx,%cx # CX will count digits
printn.1:
xorl %edx,%edx # satisfy the DIV instruction
divl %ebx # EAX = EAX / 10, EDX = EAX % 10
decw %si # --SI
incw %cx # ++CX
addb $'0',%dl # DL = decimal digit to print
movb %dl,(%si) # *SI == DL
testl %eax,%eax # did we reach zero?
jnz printn.1
call print
printn.h: jmp printn.h # halt
#endif
/*
* print - print a message on the screen
*
* parameters:
* DS:SI pointer to the message being written
* CL number of characters to write
*
* registers trashed: AL, CX, DI, ES
*/
print:
pushw %si
pushw $0xb800
popw %es
xorb %ch,%ch # high 16 bits of counter = 0
xorw %di,%di # ES:DI = b800:0000
movb $0x07,%al # AL = 0x07 (white, non-blink)
print.1:
movsb # move one byte of text
stosb # store one byte of attributes
loop print.1
popw %si
ret
/*
* fncmp - compares two 8.3 style filenames
*
* parameters:
* DS:SI pointer to one filename
* ES:DI pointer the other filename
*
* returns:
* zero flag set if filenames are equal
*
* registers trashed: CX, DI
*/
fncmp:
pushw %si
movw $FilenameLen,%cx # compare all 11 characters
repe cmpsb
popw %si # pop won't affect flags
ret
/*
* fread - reads a file given its first cluster
*
* parameters:
* EAX first cluster to read (AX on FAT12/FAT16)
* ES:DI destination buffer
*
* returns:
* ES:DI pointer past the used portion of the destination buffer
*
* registers trashed: EAX, EBX, ECX, EDX
*/
fread:
/*
* First we establish a stack frame here. We will assume, for
* size optimization, that the stack frame above is the main stack
* frame. That means this function won't work if it's not called in that
* context.
* The margin between frames contains IP and BP only (4 bytes).
*/
pushw %bp
movw %sp,%bp
fread_frame = 0
fread_wBps = main_wBps - main_frame + 4
fread_bSpc = main_bSpc - main_frame + 4
fread_sFat = main_sFat - main_frame + 4
fread_sData = main_sData - main_frame + 4
fread_cFatCache = main_cFatCache - main_frame + 4
fread.1:
/*
* Check if the cluster is something crazy, such as the last cluster in
* file or a bad sector or anything strange.
*/
#if FAT == 32
cmpl $2,%eax
jb fread.2
cmpl $0x0ffffff0,%eax
jb fread.3
#elif FAT == 16
cmpw $2,%ax
jb fread.2
cmpw $0xfff0,%ax
jb fread.3
#elif FAT == 12
cmpw $2,%ax
jb fread.2
cmpw $0x0ff0,%ax
jb fread.3
#endif
fread.2:
/*
* If the cluster number is crazy, then we return and that's it.
*/
leave
ret
fread.3:
/*
* Clean the cluster number, on FAT12 and FAT16, we may have trash
* on the upper 16 bits of EAX.
* Then save the cluster number on a local variable.
*/
fread_frame = fread_frame - 4 # 4 bytes even for FAT12/16
fread_cluster = fread_frame # new local var cluster
#if FAT == 16 || FAT == 12
movzwl %ax,%eax # zero extend AX
#endif
pushl %eax # store to var cluster
/*
* Compute the start LBA for this cluster, first the offset from the
* data region, then the LBA.
* ECX is kept with the number of sectors per cluster.
*/
#if FAT == 32
subl $2,%eax
#elif FAT == 16 || FAT == 12
decw %ax
decw %ax
#endif
movzbl fread_bSpc(%bp),%ecx # ECX = sectors per cluster
mull %ecx # EDX:EAX = (cluster - 2) * ECX
addl fread_sData(%bp),%eax # EDX:EAX = start LBA
#if defined(WITH_LBA_64BIT)
adcl fread_sData+4(%bp),%edx
#endif
/*
* Read the cluster, the number of sectors to read is bSpc (in CX).
*/
call read
/*
* After reading, advance DI by the number of bytes read and on overflow
* advance ES too.
*/
movw fread_wBps(%bp),%ax
mulw %cx # AX = bytes per cluster (bSpc * wBps)
addw %ax,%di # advance DI by AX bytes
jnc fread.4 # check for carry
pushw %es # advance ES by 64k
addw $0x1000,fread_frame-2(%bp)
popw %es
fread.4:
/*
* Now we must locate the next cluster in the file, and for that, first
* locate the FAT sector that we must read by obtaining the quotient and
* remainder of the division of the cluster number by the number of
* cluster pointers per FAT sector.
*/
popl %eax # restore from fread_cluster
fread_frame = fread_frame + 4 # fread_cluster goes away
fread_frame = fread_frame - 2
fread_index = fread_frame # new local var index
#if FAT == 32
movzwl fread_wBps(%bp),%ebx # EBX = bytes per sector
shrw $2,%bx # EBX = ptrs per sector
xorl %edx,%edx
divl %ebx # EAX = FAT sector, EDX = ptr
pushw %dx # save cluster pointer index
#elif FAT == 16
movw fread_wBps(%bp),%bx # BX = bytes per sector
shrw $1,%bx # BX = ptrs per sector
xorw %dx,%dx
divw %bx # AX = FAT sector, DX = ptr
pushw %dx # save cluster pointer index
#elif FAT == 12
movw fread_wBps(%bp),%bx # BX = bytes per sector
shlw $1,%bx # BX = ptrs per trio
xorw %dx,%dx
divw %bx # AX = FAT trio, DX = ptr
pushw %dx # save cluster pointer index
movw $3,%bx
mulw %bx # AX = first sector of trio
#endif
/*
* Check if this FAT sector was already loaded and, if so, skip to the
* next section.
*/
#if FAT == 32
cmpl fread_cFatCache(%bp),%eax
#elif FAT == 16 || FAT == 12
cmpw fread_cFatCache(%bp),%ax
#endif
je fread.5
/*
* We have to load the relevant sector of FAT. We keep the sector that
* we will load in cFatCache (if the read fails we will abort anyway).
*/
#if FAT == 32
movl %eax,fread_cFatCache(%bp)
#elif FAT == 16 || FAT == 12
movw %ax,fread_cFatCache(%bp)
#endif
xorl %edx,%edx
#if !defined(WITH_LBA_64BIT)
addl fread_sFat(%bp),%eax # EDX:EAX = FAT LBA
#else
addl fread_sFat(%bp),%eax
adcl fread_sFat+4(%bp),%edx # EDX:EAX = FAT LBA
#endif
/*
* Now read that sector into the buffer for FAT sectors (0000:7e00).
*/
pushw %es
pushw %di
pushw %cs
popw %es
movw $FatOffset,%di # ES:DI = 0000:xxxx
#if FAT == 12
movw $3,%cx # on FAT12, read 3 sectors
#else
movw $1,%cx
#endif
call read # read the FAT sector
popw %di
popw %es
fread.5:
/*
* Finally we read the cluster pointer in this FAT sector and repeat the
* whole thing.
*/
popw %bx # restore fread_index to BX
fread_frame = fread_frame + 2 # fread_index goes away
#if FAT == 32
shlw $2,%bx # BX = index * 4
movl FatOffset(%bx),%eax # EAX = next cluster in file
jmp fread.1
#elif FAT == 16
shlw $1,%bx # BX = index * 2
movw FatOffset(%bx),%ax # AX = next cluster in file
jmp fread.1
#elif FAT == 12
xorw %cx,%cx # CX = default shift factor 0
movw %bx,%ax # AX = index too
testw $1,%ax # is it even or odd?
jz fread.6 # if even, CL (shift factor) = 0
movb $4,%cl # if odd, CL (shift factor) = 4
fread.6:
shrw $1,%ax
addw %ax,%bx # BX = 3 * (index / 2) + odd?
movw FatOffset(%bx),%ax # low or high 12 of AX is ptr
shrw %cl,%ax # shift right by CL
andw $0x0fff,%ax # trim high bits
jmp fread.1
#endif
/*
* read - reads sectors by 64-bit Logical Block Address (LBA)
*
* parameters:
* EDX:EAX LBA of first sector to read
* CX number of sectors to read
* ES:DI destination buffer
*
* registers trashed: AX, DL
*/
read:
pushw %si
/*
* Fill in Disk Address Packet (DAP) structure. This is filled on the
* stack from top to bottom.
*/
pushl %edx
pushl %eax
pushw %es
pushw %di
pushw %cx
pushw $16
/*
* Call BIOS int 13h AH=42h: Extended Read Sectors From Drive.
*/
movb $0x42,%ah
movb bDrive,%dl
movw %sp,%si # SI = SP (base of DAP)
int $0x13
/*
* Return on success or halt on error (an indicative message would have
* already been written on the screen).
*/
read.h: jc read.h # Halt if carry
addw $16,%sp
popw %si
ret
/*
* String constants for the whole program and their length.
*/
#if defined(WITH_VALIDATION) && !defined(DEBUG)
.equ InvalidLen, 3
Invalid: .ascii "Bad"
#endif
.equ MissingLen, 19 # "Missing " + 8.3 filename
Missing: .ascii "Missing "
.equ FilenameLen, 11 # 8.3 filename (11)
#if FAT == 32
EfiLdr: .ascii "EFILDR20 "
#elif FAT == 16
EfiLdr: .ascii "EFILDR16 "
#elif FAT == 12
EfiLdr: .ascii "EFILDR "
#endif
EfiVar: .ascii "EFIVAR BIN"
/*
* Since the BPB only has room for a 32-bit number of hidden sectors, if we need
* 64-bit LBA, we need room for storing the high-order 32 bits of hidden
* sectors.
*/
#if defined(WITH_LBA_64BIT)
.org 0x01fa
lHiddenHigh: .long 0
#endif
/*
* The boot sector signature at the end of the sector.
*/
.org 0x01fe
wSignature: .word 0xaa55