mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-26 16:47:40 +01:00
942 lines
25 KiB
ArmAsm
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
|