#- # Copyright (c) 2007 Yahoo!, Inc. # All rights reserved. # Written by: John Baldwin # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the author nor the names of any co-contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # # Partly from: src/sys/boot/i386/mbr/mbr.s 1.7 # A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition # and boots it. .set LOAD,0x7c00 # Load address .set EXEC,0x600 # Execution address .set MAGIC,0xaa55 # Magic: bootable .set SECSIZE,0x200 # Size of a single disk sector .set DISKSIG,440 # Disk signature offset .set STACK,EXEC+SECSIZE*4 # Stack address .set GPT_ADDR,STACK # GPT header address .set GPT_SIG,0 .set GPT_SIG_0,0x20494645 # "EFI " .set GPT_SIG_1,0x54524150 # "PART" .set GPT_MYLBA,24 .set GPT_PART_LBA,72 .set GPT_NPART,80 .set GPT_PART_SIZE,84 .set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address .set PART_TYPE,0 .set PART_START_LBA,32 .set PART_END_LBA,40 .set DPBUF,PART_ADDR+SECSIZE .set DPBUF_SEC,0x10 # Number of sectors .set NHRDRV,0x475 # Number of hard drives .globl start # Entry point .code16 # # Setup the segment registers for flat addressing and setup the stack. # start: cld # String ops inc xorw %ax,%ax # Zero movw %ax,%es # Address movw %ax,%ds # data movw %ax,%ss # Set up movw $STACK,%sp # stack # # Relocate ourself to a lower address so that we have more room to load # other sectors. # movw $main-EXEC+LOAD,%si # Source movw $main,%di # Destination movw $SECSIZE-(main-start),%cx # Byte count rep # Relocate movsb # code # # Jump to the relocated code. # jmp main-LOAD+EXEC # To relocated code # # Validate drive number in %dl. # main: cmpb $0x80,%dl # Drive valid? jb main.1 # No movb NHRDRV,%dh # Calculate the highest addb $0x80,%dh # drive number available cmpb %dh,%dl # Within range? jb main.2 # Yes main.1: movb $0x80,%dl # Assume drive 0x80 # # Load the GPT header and verify signature. Try LBA 1 for the primary one and # the last LBA for the backup if it is broken. # main.2: call getdrvparams # Read drive parameters movb $1,%dh # %dh := 1 (reading primary) main.2a: movw $GPT_ADDR,%bx movw $lba,%si call read # Read header and check GPT sig cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG jnz main.2b cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4 jnz main.2b jmp load_part main.2b: cmpb $1,%dh # Reading primary? jne err_pt # If no - invalid table found # # Try alternative LBAs from the last sector for the GPT header. # main.3: movb $0,%dh # %dh := 0 (reading backup) movw $DPBUF+DPBUF_SEC,%si # %si = last sector + 1 movw $lba,%di # %di = $lba main.3a: subl $1, (%si) # 0x0(%si) = last sec (0-31) sbbl $0, 4(%si) movw $4,%cx rep movsw # $lastsec--, copy it to $lba jmp main.2a # Read the next sector # # Load a partition table sector from disk and look for a FreeBSD boot # partition. # load_part: movw $GPT_ADDR+GPT_PART_LBA,%si movw $PART_ADDR,%bx call read scan: movw %bx,%si # Compare partition UUID movw $boot_uuid,%di # with FreeBSD boot UUID movw $0x10,%cx repe cmpsb jnz next_part # Didn't match, next partition # # We found a boot partition. Load it into RAM starting at 0x7c00. # movw %bx,%di # Save partition pointer in %di leaw PART_START_LBA(%di),%si movw $LOAD/16,%bx movw %bx,%es xorw %bx,%bx load_boot: push %si # Save %si call read pop %si # Restore movl PART_END_LBA(%di),%eax # See if this was the last LBA cmpl (%si),%eax jnz next_boot movl PART_END_LBA+4(%di),%eax cmpl 4(%si),%eax jnz next_boot mov %bx,%es # Reset %es to zero jmp LOAD # Jump to boot code next_boot: addl $1,(%si) # Next LBA adcl $0,4(%si) mov %es,%ax # Adjust segment for next addw $SECSIZE/16,%ax # sector cmp $0x9000,%ax # Don't load past 0x90000, jb sz_ok # 545k should be enough for call err_big # any boot code, but warn mov $0x9000-SECSIZE/16,%ax # and truncate sz_ok: mov %ax,%es jmp load_boot # # Move to the next partition. If we walk off the end of the sector, load # the next sector. We assume that partition entries are smaller than 64k # and that they won't span a sector boundary. # # XXX: Should we int 0x18 instead of err_noboot if we hit the end of the table? # next_part: decl GPT_ADDR+GPT_NPART # Was this the last partition? jz err_noboot movw GPT_ADDR+GPT_PART_SIZE,%ax addw %ax,%bx # Next partition cmpw $PART_ADDR+0x200,%bx # Still in sector? jb scan addl $1, GPT_ADDR+GPT_PART_LBA # Next sector adcl $0,GPT_ADDR+GPT_PART_LBA+4 jmp load_part # # Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating # a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si. # read: pushl 0x4(%si) # Set the LBA pushl 0x0(%si) # address pushw %es # Set the address of pushw %bx # the transfer buffer pushw $0x1 # Read 1 sector pushw $0x10 # Packet length movw %sp,%si # Packer pointer movw $0x4200,%ax # BIOS: LBA Read from disk int $0x13 # Call the BIOS add $0x10,%sp # Restore stack jc err_rd # If error ret # # Check the number of LBAs on the drive index %dx. Trashes %ax and %si. # getdrvparams: movw $DPBUF,%si # Set the address of result buf movw $0x001e,(%si) # len movw $0x4800,%ax # BIOS: Read Drive Parameters int $0x13 # Call the BIOS jc err_rd # "I/O error" if error ret # # Various error message entry points. # err_big: movw $msg_big,%si # "Truncated call putstr # to 545k" ret err_pt: movw $msg_pt,%si # "Invalid partition call putstr # table" err_pt.1: jmp err_pt.1 # Await reset err_rd: movw $msg_rd,%si # "I/O error loading call putstr # boot loader" jmp err_pt.1 err_noboot: movw $msg_noboot,%si # "Missing boot call putstr # loader" jmp err_pt.1 # # Output an ASCIZ string to the console via the BIOS. # putstr.0: movw $0x7,%bx # Page:attribute movb $0xe,%ah # BIOS: Display int $0x10 # character putstr: lodsb # Get character testb %al,%al # End of string? jnz putstr.0 # No ret msg_big: .asciz "Loaded only 545k" msg_pt: .asciz "Invalid partition table" msg_rd: .asciz "I/O error loading boot loader" msg_noboot: .asciz "Missing boot loader" lba: .quad 1 # LBA of GPT header boot_uuid: .long 0x83bd6b9d .word 0x7f41 .word 0x11dc .byte 0xbe .byte 0x0b .byte 0x00 .byte 0x15 .byte 0x60 .byte 0xb8 .byte 0x4f .byte 0x0f .org DISKSIG,0x90 sig: .long 0 # OS Disk Signature .word 0 # "Unknown" in PMBR partbl: .fill 0x10,0x4,0x0 # Partition table .word MAGIC # Magic number