1#- 2# Copyright (c) 2007 Yahoo!, Inc. 3# All rights reserved. 4# Written by: John Baldwin <jhb@FreeBSD.org> 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 3. Neither the name of the author nor the names of any co-contributors 15# may be used to endorse or promote products derived from this software 16# without specific prior written permission. 17# 18# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28# SUCH DAMAGE. 29# 30# $FreeBSD$ 31# 32# Partly from: src/sys/boot/i386/mbr/mbr.s 1.7 33 34# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition 35# and boots it. 36 37 .set LOAD,0x7c00 # Load address 38 .set EXEC,0x600 # Execution address 39 .set MAGIC,0xaa55 # Magic: bootable 40 .set SECSIZE,0x200 # Size of a single disk sector 41 .set DISKSIG,440 # Disk signature offset 42 .set STACK,EXEC+SECSIZE*4 # Stack address 43 .set GPT_ADDR,STACK # GPT header address 44 .set GPT_SIG,0 45 .set GPT_SIG_0,0x20494645 # "EFI " 46 .set GPT_SIG_1,0x54524150 # "PART" 47 .set GPT_MYLBA,24 48 .set GPT_PART_LBA,72 49 .set GPT_NPART,80 50 .set GPT_PART_SIZE,84 51 .set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address 52 .set PART_TYPE,0 53 .set PART_START_LBA,32 54 .set PART_END_LBA,40 55 .set DPBUF,PART_ADDR+SECSIZE 56 .set DPBUF_SEC,0x10 # Number of sectors 57 58 .set NHRDRV,0x475 # Number of hard drives 59 60 .globl start # Entry point 61 .code16 62 63# 64# Setup the segment registers for flat addressing and setup the stack. 65# 66start: cld # String ops inc 67 xorw %ax,%ax # Zero 68 movw %ax,%es # Address 69 movw %ax,%ds # data 70 movw %ax,%ss # Set up 71 movw $STACK,%sp # stack 72# 73# Relocate ourself to a lower address so that we have more room to load 74# other sectors. 75# 76 movw $main-EXEC+LOAD,%si # Source 77 movw $main,%di # Destination 78 movw $SECSIZE-(main-start),%cx # Byte count 79 rep # Relocate 80 movsb # code 81# 82# Jump to the relocated code. 83# 84 jmp main-LOAD+EXEC # To relocated code 85# 86# Validate drive number in %dl. 87# 88main: cmpb $0x80,%dl # Drive valid? 89 jb main.1 # No 90 movb NHRDRV,%dh # Calculate the highest 91 addb $0x80,%dh # drive number available 92 cmpb %dh,%dl # Within range? 93 jb main.2 # Yes 94main.1: movb $0x80,%dl # Assume drive 0x80 95# 96# Load the GPT header and verify signature. Try LBA 1 for the primary one and 97# the last LBA for the backup if it is broken. 98# 99main.2: call getdrvparams # Read drive parameters 100 movb $1,%dh # %dh := 1 (reading primary) 101main.2a: movw $GPT_ADDR,%bx 102 movw $lba,%si 103 call read # Read header and check GPT sig 104 cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG 105 jnz main.2b 106 cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4 107 jnz main.2b 108 jmp load_part 109main.2b: cmpb $1,%dh # Reading primary? 110 jne err_pt # If no - invalid table found 111# 112# Try alternative LBAs from the last sector for the GPT header. 113# 114main.3: movb $0,%dh # %dh := 0 (reading backup) 115 movw $DPBUF+DPBUF_SEC,%si # %si = last sector + 1 116 movw $lba,%di # %di = $lba 117main.3a: decl (%si) # 0x0(%si) = last sec (0-31) 118 movw $2,%cx 119 rep 120 movsw # $lastsec--, copy it to $lba 121 jmp main.2a # Read the next sector 122# 123# Load a partition table sector from disk and look for a FreeBSD boot 124# partition. 125# 126load_part: movw $GPT_ADDR+GPT_PART_LBA,%si 127 movw $PART_ADDR,%bx 128 call read 129scan: movw %bx,%si # Compare partition UUID 130 movw $boot_uuid,%di # with FreeBSD boot UUID 131 movb $0x10,%cl 132 repe cmpsb 133 jnz next_part # Didn't match, next partition 134# 135# We found a boot partition. Load it into RAM starting at 0x7c00. 136# 137 movw %bx,%di # Save partition pointer in %di 138 leaw PART_START_LBA(%di),%si 139 movw $LOAD/16,%bx 140 movw %bx,%es 141 xorw %bx,%bx 142load_boot: push %si # Save %si 143 call read 144 pop %si # Restore 145 movl PART_END_LBA(%di),%eax # See if this was the last LBA 146 cmpl (%si),%eax 147 jnz next_boot 148 movl PART_END_LBA+4(%di),%eax 149 cmpl 4(%si),%eax 150 jnz next_boot 151 mov %bx,%es # Reset %es to zero 152 jmp LOAD # Jump to boot code 153next_boot: incl (%si) # Next LBA 154 adcl $0,4(%si) 155 mov %es,%ax # Adjust segment for next 156 addw $SECSIZE/16,%ax # sector 157 cmp $0x9000,%ax # Don't load past 0x90000, 158 jae err_big # 545k should be enough for 159 mov %ax,%es # any boot code. :) 160 jmp load_boot 161# 162# Move to the next partition. If we walk off the end of the sector, load 163# the next sector. We assume that partition entries are smaller than 64k 164# and that they won't span a sector boundary. 165# 166# XXX: Should we int 0x18 instead of err_noboot if we hit the end of the table? 167# 168next_part: decl GPT_ADDR+GPT_NPART # Was this the last partition? 169 jz err_noboot 170 movw GPT_ADDR+GPT_PART_SIZE,%ax 171 addw %ax,%bx # Next partition 172 cmpw $PART_ADDR+0x200,%bx # Still in sector? 173 jb scan 174 incl GPT_ADDR+GPT_PART_LBA # Next sector 175 adcl $0,GPT_ADDR+GPT_PART_LBA+4 176 jmp load_part 177# 178# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating 179# a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si. 180# 181read: pushl 0x4(%si) # Set the LBA 182 pushl 0x0(%si) # address 183 pushw %es # Set the address of 184 pushw %bx # the transfer buffer 185 pushw $0x1 # Read 1 sector 186 pushw $0x10 # Packet length 187 movw %sp,%si # Packer pointer 188 movw $0x4200,%ax # BIOS: LBA Read from disk 189 int $0x13 # Call the BIOS 190 add $0x10,%sp # Restore stack 191 jc err_rd # If error 192 ret 193# 194# Check the number of LBAs on the drive index %dx. Trashes %ax and %si. 195# 196getdrvparams: 197 movw $DPBUF,%si # Set the address of result buf 198 movw $0x001e,(%si) # len 199 movw $0x4800,%ax # BIOS: Read Drive Parameters 200 int $0x13 # Call the BIOS 201 jc err_rd # "I/O error" if error 202 ret 203# 204# Various error message entry points. 205# 206err_big: movw $msg_big,%si # "Boot loader too 207 jmp putstr # large" 208 209err_pt: movw $msg_pt,%si # "Invalid partition 210 jmp putstr # table" 211 212err_rd: movw $msg_rd,%si # "I/O error loading 213 jmp putstr # boot loader" 214 215err_noboot: movw $msg_noboot,%si # "Missing boot 216 jmp putstr # loader" 217# 218# Output an ASCIZ string to the console via the BIOS. 219# 220putstr.0: movw $0x7,%bx # Page:attribute 221 movb $0xe,%ah # BIOS: Display 222 int $0x10 # character 223putstr: lodsb # Get character 224 testb %al,%al # End of string? 225 jnz putstr.0 # No 226putstr.1: jmp putstr.1 # Await reset 227 228msg_big: .asciz "Boot loader too large" 229msg_pt: .asciz "Invalid partition table" 230msg_rd: .asciz "I/O error loading boot loader" 231msg_noboot: .asciz "Missing boot loader" 232 233lba: .quad 1 # LBA of GPT header 234 235boot_uuid: .long 0x83bd6b9d 236 .word 0x7f41 237 .word 0x11dc 238 .byte 0xbe 239 .byte 0x0b 240 .byte 0x00 241 .byte 0x15 242 .byte 0x60 243 .byte 0xb8 244 .byte 0x4f 245 .byte 0x0f 246 247 .org DISKSIG,0x90 248sig: .long 0 # OS Disk Signature 249 .word 0 # "Unknown" in PMBR 250 251partbl: .fill 0x10,0x4,0x0 # Partition table 252 .word MAGIC # Magic number 253