1172940Sjhb#- 2172940Sjhb# Copyright (c) 2007 Yahoo!, Inc. 3172940Sjhb# All rights reserved. 4172940Sjhb# Written by: John Baldwin <jhb@FreeBSD.org> 5172940Sjhb# 6172940Sjhb# Redistribution and use in source and binary forms, with or without 7172940Sjhb# modification, are permitted provided that the following conditions 8172940Sjhb# are met: 9172940Sjhb# 1. Redistributions of source code must retain the above copyright 10172940Sjhb# notice, this list of conditions and the following disclaimer. 11172940Sjhb# 2. Redistributions in binary form must reproduce the above copyright 12172940Sjhb# notice, this list of conditions and the following disclaimer in the 13172940Sjhb# documentation and/or other materials provided with the distribution. 14172940Sjhb# 3. Neither the name of the author nor the names of any co-contributors 15172940Sjhb# may be used to endorse or promote products derived from this software 16172940Sjhb# without specific prior written permission. 17172940Sjhb# 18172940Sjhb# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19172940Sjhb# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20172940Sjhb# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21172940Sjhb# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22172940Sjhb# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23172940Sjhb# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24172940Sjhb# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25172940Sjhb# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26172940Sjhb# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27172940Sjhb# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28172940Sjhb# SUCH DAMAGE. 29172940Sjhb# 30172940Sjhb# $FreeBSD: stable/11/stand/i386/pmbr/pmbr.s 239060 2012-08-05 12:57:38Z ae $ 31173957Sjhb# 32173957Sjhb# Partly from: src/sys/boot/i386/mbr/mbr.s 1.7 33172940Sjhb 34172940Sjhb# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition 35172940Sjhb# and boots it. 36172940Sjhb 37172940Sjhb .set LOAD,0x7c00 # Load address 38172940Sjhb .set EXEC,0x600 # Execution address 39172940Sjhb .set MAGIC,0xaa55 # Magic: bootable 40172940Sjhb .set SECSIZE,0x200 # Size of a single disk sector 41172940Sjhb .set DISKSIG,440 # Disk signature offset 42172940Sjhb .set STACK,EXEC+SECSIZE*4 # Stack address 43172940Sjhb .set GPT_ADDR,STACK # GPT header address 44172940Sjhb .set GPT_SIG,0 45239060Sae .set GPT_SIG_0,0x20494645 # "EFI " 46239060Sae .set GPT_SIG_1,0x54524150 # "PART" 47172940Sjhb .set GPT_MYLBA,24 48172940Sjhb .set GPT_PART_LBA,72 49172940Sjhb .set GPT_NPART,80 50172940Sjhb .set GPT_PART_SIZE,84 51172940Sjhb .set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address 52172940Sjhb .set PART_TYPE,0 53172940Sjhb .set PART_START_LBA,32 54172940Sjhb .set PART_END_LBA,40 55239060Sae .set DPBUF,PART_ADDR+SECSIZE 56239060Sae .set DPBUF_SEC,0x10 # Number of sectors 57172940Sjhb 58172940Sjhb .set NHRDRV,0x475 # Number of hard drives 59172940Sjhb 60172940Sjhb .globl start # Entry point 61172940Sjhb .code16 62172940Sjhb 63172940Sjhb# 64172940Sjhb# Setup the segment registers for flat addressing and setup the stack. 65172940Sjhb# 66172940Sjhbstart: cld # String ops inc 67172940Sjhb xorw %ax,%ax # Zero 68172940Sjhb movw %ax,%es # Address 69172940Sjhb movw %ax,%ds # data 70172940Sjhb movw %ax,%ss # Set up 71172940Sjhb movw $STACK,%sp # stack 72172940Sjhb# 73172940Sjhb# Relocate ourself to a lower address so that we have more room to load 74172940Sjhb# other sectors. 75172940Sjhb# 76172940Sjhb movw $main-EXEC+LOAD,%si # Source 77172940Sjhb movw $main,%di # Destination 78172940Sjhb movw $SECSIZE-(main-start),%cx # Byte count 79172940Sjhb rep # Relocate 80172940Sjhb movsb # code 81172940Sjhb# 82172940Sjhb# Jump to the relocated code. 83172940Sjhb# 84172940Sjhb jmp main-LOAD+EXEC # To relocated code 85172940Sjhb# 86172940Sjhb# Validate drive number in %dl. 87172940Sjhb# 88172940Sjhbmain: cmpb $0x80,%dl # Drive valid? 89172940Sjhb jb main.1 # No 90172940Sjhb movb NHRDRV,%dh # Calculate the highest 91172940Sjhb addb $0x80,%dh # drive number available 92172940Sjhb cmpb %dh,%dl # Within range? 93172940Sjhb jb main.2 # Yes 94172940Sjhbmain.1: movb $0x80,%dl # Assume drive 0x80 95172940Sjhb# 96239060Sae# Load the GPT header and verify signature. Try LBA 1 for the primary one and 97239060Sae# the last LBA for the backup if it is broken. 98172940Sjhb# 99239060Saemain.2: call getdrvparams # Read drive parameters 100239060Sae movb $1,%dh # %dh := 1 (reading primary) 101239060Saemain.2a: movw $GPT_ADDR,%bx 102172940Sjhb movw $lba,%si 103239060Sae call read # Read header and check GPT sig 104172940Sjhb cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG 105239060Sae jnz main.2b 106172940Sjhb cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4 107239060Sae jnz main.2b 108239060Sae jmp load_part 109239060Saemain.2b: cmpb $1,%dh # Reading primary? 110239060Sae jne err_pt # If no - invalid table found 111172940Sjhb# 112239060Sae# Try alternative LBAs from the last sector for the GPT header. 113239060Sae# 114239060Saemain.3: movb $0,%dh # %dh := 0 (reading backup) 115239060Sae movw $DPBUF+DPBUF_SEC,%si # %si = last sector + 1 116239060Sae movw $lba,%di # %di = $lba 117239060Saemain.3a: decl (%si) # 0x0(%si) = last sec (0-31) 118239060Sae movw $2,%cx 119239060Sae rep 120239060Sae movsw # $lastsec--, copy it to $lba 121239060Sae jmp main.2a # Read the next sector 122239060Sae# 123172940Sjhb# Load a partition table sector from disk and look for a FreeBSD boot 124172940Sjhb# partition. 125172940Sjhb# 126172940Sjhbload_part: movw $GPT_ADDR+GPT_PART_LBA,%si 127172940Sjhb movw $PART_ADDR,%bx 128172940Sjhb call read 129172940Sjhbscan: movw %bx,%si # Compare partition UUID 130172940Sjhb movw $boot_uuid,%di # with FreeBSD boot UUID 131172940Sjhb movb $0x10,%cl 132172940Sjhb repe cmpsb 133172940Sjhb jnz next_part # Didn't match, next partition 134172940Sjhb# 135172940Sjhb# We found a boot partition. Load it into RAM starting at 0x7c00. 136172940Sjhb# 137172940Sjhb movw %bx,%di # Save partition pointer in %di 138172940Sjhb leaw PART_START_LBA(%di),%si 139172940Sjhb movw $LOAD/16,%bx 140172940Sjhb movw %bx,%es 141172940Sjhb xorw %bx,%bx 142172940Sjhbload_boot: push %si # Save %si 143172940Sjhb call read 144172940Sjhb pop %si # Restore 145172940Sjhb movl PART_END_LBA(%di),%eax # See if this was the last LBA 146172940Sjhb cmpl (%si),%eax 147172940Sjhb jnz next_boot 148172940Sjhb movl PART_END_LBA+4(%di),%eax 149172940Sjhb cmpl 4(%si),%eax 150172940Sjhb jnz next_boot 151172940Sjhb mov %bx,%es # Reset %es to zero 152172940Sjhb jmp LOAD # Jump to boot code 153172940Sjhbnext_boot: incl (%si) # Next LBA 154172940Sjhb adcl $0,4(%si) 155172940Sjhb mov %es,%ax # Adjust segment for next 156172940Sjhb addw $SECSIZE/16,%ax # sector 157172940Sjhb cmp $0x9000,%ax # Don't load past 0x90000, 158172940Sjhb jae err_big # 545k should be enough for 159172940Sjhb mov %ax,%es # any boot code. :) 160172940Sjhb jmp load_boot 161172940Sjhb# 162172940Sjhb# Move to the next partition. If we walk off the end of the sector, load 163172940Sjhb# the next sector. We assume that partition entries are smaller than 64k 164172940Sjhb# and that they won't span a sector boundary. 165172940Sjhb# 166172940Sjhb# XXX: Should we int 0x18 instead of err_noboot if we hit the end of the table? 167172940Sjhb# 168172940Sjhbnext_part: decl GPT_ADDR+GPT_NPART # Was this the last partition? 169172940Sjhb jz err_noboot 170172940Sjhb movw GPT_ADDR+GPT_PART_SIZE,%ax 171172940Sjhb addw %ax,%bx # Next partition 172172940Sjhb cmpw $PART_ADDR+0x200,%bx # Still in sector? 173172940Sjhb jb scan 174172940Sjhb incl GPT_ADDR+GPT_PART_LBA # Next sector 175172940Sjhb adcl $0,GPT_ADDR+GPT_PART_LBA+4 176172940Sjhb jmp load_part 177172940Sjhb# 178172940Sjhb# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating 179172940Sjhb# a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si. 180172940Sjhb# 181172940Sjhbread: pushl 0x4(%si) # Set the LBA 182172940Sjhb pushl 0x0(%si) # address 183172940Sjhb pushw %es # Set the address of 184172940Sjhb pushw %bx # the transfer buffer 185172940Sjhb pushw $0x1 # Read 1 sector 186172940Sjhb pushw $0x10 # Packet length 187172940Sjhb movw %sp,%si # Packer pointer 188172940Sjhb movw $0x4200,%ax # BIOS: LBA Read from disk 189172940Sjhb int $0x13 # Call the BIOS 190172940Sjhb add $0x10,%sp # Restore stack 191172940Sjhb jc err_rd # If error 192172940Sjhb ret 193172940Sjhb# 194239060Sae# Check the number of LBAs on the drive index %dx. Trashes %ax and %si. 195239060Sae# 196239060Saegetdrvparams: 197239060Sae movw $DPBUF,%si # Set the address of result buf 198239060Sae movw $0x001e,(%si) # len 199239060Sae movw $0x4800,%ax # BIOS: Read Drive Parameters 200239060Sae int $0x13 # Call the BIOS 201239060Sae jc err_rd # "I/O error" if error 202239060Sae ret 203239060Sae# 204172940Sjhb# Various error message entry points. 205172940Sjhb# 206172940Sjhberr_big: movw $msg_big,%si # "Boot loader too 207172940Sjhb jmp putstr # large" 208172940Sjhb 209172940Sjhberr_pt: movw $msg_pt,%si # "Invalid partition 210172940Sjhb jmp putstr # table" 211172940Sjhb 212172940Sjhberr_rd: movw $msg_rd,%si # "I/O error loading 213172940Sjhb jmp putstr # boot loader" 214172940Sjhb 215172940Sjhberr_noboot: movw $msg_noboot,%si # "Missing boot 216172940Sjhb jmp putstr # loader" 217172940Sjhb# 218172940Sjhb# Output an ASCIZ string to the console via the BIOS. 219172940Sjhb# 220172940Sjhbputstr.0: movw $0x7,%bx # Page:attribute 221172940Sjhb movb $0xe,%ah # BIOS: Display 222172940Sjhb int $0x10 # character 223172940Sjhbputstr: lodsb # Get character 224172940Sjhb testb %al,%al # End of string? 225172940Sjhb jnz putstr.0 # No 226172940Sjhbputstr.1: jmp putstr.1 # Await reset 227172940Sjhb 228172940Sjhbmsg_big: .asciz "Boot loader too large" 229172940Sjhbmsg_pt: .asciz "Invalid partition table" 230172940Sjhbmsg_rd: .asciz "I/O error loading boot loader" 231172940Sjhbmsg_noboot: .asciz "Missing boot loader" 232172940Sjhb 233172940Sjhblba: .quad 1 # LBA of GPT header 234172940Sjhb 235172940Sjhbboot_uuid: .long 0x83bd6b9d 236172940Sjhb .word 0x7f41 237172940Sjhb .word 0x11dc 238172940Sjhb .byte 0xbe 239172940Sjhb .byte 0x0b 240172940Sjhb .byte 0x00 241172940Sjhb .byte 0x15 242172940Sjhb .byte 0x60 243172940Sjhb .byte 0xb8 244172940Sjhb .byte 0x4f 245172940Sjhb .byte 0x0f 246172940Sjhb 247172940Sjhb .org DISKSIG,0x90 248172940Sjhbsig: .long 0 # OS Disk Signature 249172940Sjhb .word 0 # "Unknown" in PMBR 250172940Sjhb 251172940Sjhbpartbl: .fill 0x10,0x4,0x0 # Partition table 252172940Sjhb .word MAGIC # Magic number 253