#-1# Copyright (c) 2007 Yahoo!, Inc.2# All rights reserved.3# Written by: John Baldwin <[email protected]>4#5# Redistribution and use in source and binary forms, with or without6# modification, are permitted provided that the following conditions7# are met:8# 1. Redistributions of source code must retain the above copyright9# notice, this list of conditions and the following disclaimer.10# 2. Redistributions in binary form must reproduce the above copyright11# notice, this list of conditions and the following disclaimer in the12# documentation and/or other materials provided with the distribution.13# 3. Neither the name of the author nor the names of any co-contributors14# may be used to endorse or promote products derived from this software15# without specific prior written permission.16#17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27# SUCH DAMAGE.28#29#30# Partly from: src/sys/boot/i386/mbr/mbr.s 1.73132# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition33# and boots it.3435.set LOAD,0x7c00 # Load address36.set EXEC,0x600 # Execution address37.set MAGIC,0xaa55 # Magic: bootable38.set SECSIZE,0x200 # Size of a single disk sector39.set DISKSIG,440 # Disk signature offset40.set STACK,EXEC+SECSIZE*4 # Stack address41.set GPT_ADDR,STACK # GPT header address42.set GPT_SIG,043.set GPT_SIG_0,0x20494645 # "EFI "44.set GPT_SIG_1,0x54524150 # "PART"45.set GPT_MYLBA,2446.set GPT_PART_LBA,7247.set GPT_NPART,8048.set GPT_PART_SIZE,8449.set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address50.set PART_TYPE,051.set PART_START_LBA,3252.set PART_END_LBA,4053.set DPBUF,PART_ADDR+SECSIZE54.set DPBUF_SEC,0x10 # Number of sectors5556.set NHRDRV,0x475 # Number of hard drives5758.globl start # Entry point59.code166061#62# Setup the segment registers for flat addressing and setup the stack.63#64start: cld # String ops inc65xorw %ax,%ax # Zero66movw %ax,%es # Address67movw %ax,%ds # data68movw %ax,%ss # Set up69movw $STACK,%sp # stack70#71# Relocate ourself to a lower address so that we have more room to load72# other sectors.73#74movw $main-EXEC+LOAD,%si # Source75movw $main,%di # Destination76movw $SECSIZE-(main-start),%cx # Byte count77rep # Relocate78movsb # code79#80# Jump to the relocated code.81#82jmp main-LOAD+EXEC # To relocated code83#84# Validate drive number in %dl.85#86main: cmpb $0x80,%dl # Drive valid?87jb main.1 # No88movb NHRDRV,%dh # Calculate the highest89addb $0x80,%dh # drive number available90cmpb %dh,%dl # Within range?91jb main.2 # Yes92main.1: movb $0x80,%dl # Assume drive 0x8093#94# Load the GPT header and verify signature. Try LBA 1 for the primary one and95# the last LBA for the backup if it is broken.96#97main.2: call getdrvparams # Read drive parameters98movb $1,%dh # %dh := 1 (reading primary)99main.2a: movw $GPT_ADDR,%bx100movw $lba,%si101call read # Read header and check GPT sig102cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG103jnz main.2b104cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4105jnz main.2b106jmp load_part107main.2b: cmpb $1,%dh # Reading primary?108jne err_pt # If no - invalid table found109#110# Try alternative LBAs from the last sector for the GPT header.111#112main.3: movb $0,%dh # %dh := 0 (reading backup)113movw $DPBUF+DPBUF_SEC,%si # %si = last sector + 1114movw $lba,%di # %di = $lba115main.3a: subl $1, (%si) # 0x0(%si) = last sec (0-31)116sbbl $0, 4(%si)117movw $4,%cx118rep119movsw # $lastsec--, copy it to $lba120jmp main.2a # Read the next sector121#122# Load a partition table sector from disk and look for a FreeBSD boot123# partition.124#125load_part: movw $GPT_ADDR+GPT_PART_LBA,%si126movw $PART_ADDR,%bx127call read128scan: movw %bx,%si # Compare partition UUID129movw $boot_uuid,%di # with FreeBSD boot UUID130movw $0x10,%cx131repe cmpsb132jnz next_part # Didn't match, next partition133#134# We found a boot partition. Load it into RAM starting at 0x7c00.135#136movw %bx,%di # Save partition pointer in %di137leaw PART_START_LBA(%di),%si138movw $LOAD/16,%bx139movw %bx,%es140xorw %bx,%bx141load_boot: push %si # Save %si142call read143pop %si # Restore144movl PART_END_LBA(%di),%eax # See if this was the last LBA145cmpl (%si),%eax146jnz next_boot147movl PART_END_LBA+4(%di),%eax148cmpl 4(%si),%eax149jnz next_boot150mov %bx,%es # Reset %es to zero151jmp LOAD # Jump to boot code152next_boot: addl $1,(%si) # Next LBA153adcl $0,4(%si)154mov %es,%ax # Adjust segment for next155addw $SECSIZE/16,%ax # sector156cmp $0x9000,%ax # Don't load past 0x90000,157jb sz_ok # 545k should be enough for158call err_big # any boot code, but warn159mov $0x9000-SECSIZE/16,%ax # and truncate160sz_ok: mov %ax,%es161jmp load_boot162#163# Move to the next partition. If we walk off the end of the sector, load164# the next sector. We assume that partition entries are smaller than 64k165# and that they won't span a sector boundary.166#167# XXX: Should we int 0x18 instead of err_noboot if we hit the end of the table?168#169next_part: decl GPT_ADDR+GPT_NPART # Was this the last partition?170jz err_noboot171movw GPT_ADDR+GPT_PART_SIZE,%ax172addw %ax,%bx # Next partition173cmpw $PART_ADDR+0x200,%bx # Still in sector?174jb scan175addl $1, GPT_ADDR+GPT_PART_LBA # Next sector176adcl $0,GPT_ADDR+GPT_PART_LBA+4177jmp load_part178#179# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating180# a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si.181#182read: pushl 0x4(%si) # Set the LBA183pushl 0x0(%si) # address184pushw %es # Set the address of185pushw %bx # the transfer buffer186pushw $0x1 # Read 1 sector187pushw $0x10 # Packet length188movw %sp,%si # Packer pointer189movw $0x4200,%ax # BIOS: LBA Read from disk190int $0x13 # Call the BIOS191add $0x10,%sp # Restore stack192jc err_rd # If error193ret194#195# Check the number of LBAs on the drive index %dx. Trashes %ax and %si.196#197getdrvparams:198movw $DPBUF,%si # Set the address of result buf199movw $0x001e,(%si) # len200movw $0x4800,%ax # BIOS: Read Drive Parameters201int $0x13 # Call the BIOS202jc err_rd # "I/O error" if error203ret204#205# Various error message entry points.206#207err_big: movw $msg_big,%si # "Truncated208call putstr # to 545k"209ret210211err_pt: movw $msg_pt,%si # "Invalid partition212call putstr # table"213err_pt.1: jmp err_pt.1 # Await reset214215err_rd: movw $msg_rd,%si # "I/O error loading216call putstr # boot loader"217jmp err_pt.1218219err_noboot: movw $msg_noboot,%si # "Missing boot220call putstr # loader"221jmp err_pt.1222#223# Output an ASCIZ string to the console via the BIOS.224#225putstr.0: movw $0x7,%bx # Page:attribute226movb $0xe,%ah # BIOS: Display227int $0x10 # character228putstr: lodsb # Get character229testb %al,%al # End of string?230jnz putstr.0 # No231ret232233msg_big: .asciz "Loaded only 545k"234msg_pt: .asciz "Invalid partition table"235msg_rd: .asciz "I/O error loading boot loader"236msg_noboot: .asciz "Missing boot loader"237238lba: .quad 1 # LBA of GPT header239240boot_uuid: .long 0x83bd6b9d241.word 0x7f41242.word 0x11dc243.byte 0xbe244.byte 0x0b245.byte 0x00246.byte 0x15247.byte 0x60248.byte 0xb8249.byte 0x4f250.byte 0x0f251252.org DISKSIG,0x90253sig: .long 0 # OS Disk Signature254.word 0 # "Unknown" in PMBR255256partbl: .fill 0x10,0x4,0x0 # Partition table257.word MAGIC # Magic number258259260