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# 31# Partly from: src/sys/boot/i386/mbr/mbr.s 1.7 32 33# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition 34# and boots it. 35 36 .set LOAD,0x7c00 # Load address 37 .set EXEC,0x600 # Execution address 38 .set MAGIC,0xaa55 # Magic: bootable 39 .set SECSIZE,0x200 # Size of a single disk sector 40 .set DISKSIG,440 # Disk signature offset 41 .set STACK,EXEC+SECSIZE*4 # Stack address 42 .set GPT_ADDR,STACK # GPT header address 43 .set GPT_SIG,0 44 .set GPT_SIG_0,0x20494645 # "EFI " 45 .set GPT_SIG_1,0x54524150 # "PART" 46 .set GPT_MYLBA,24 47 .set GPT_PART_LBA,72 48 .set GPT_NPART,80 49 .set GPT_PART_SIZE,84 50 .set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address 51 .set PART_TYPE,0 52 .set PART_START_LBA,32 53 .set PART_END_LBA,40 54 .set DPBUF,PART_ADDR+SECSIZE 55 .set DPBUF_SEC,0x10 # Number of sectors 56 57 .set NHRDRV,0x475 # Number of hard drives 58 59 .globl start # Entry point 60 .code16 61 62# 63# Setup the segment registers for flat addressing and setup the stack. 64# 65start: cld # String ops inc 66 xorw %ax,%ax # Zero 67 movw %ax,%es # Address 68 movw %ax,%ds # data 69 movw %ax,%ss # Set up 70 movw $STACK,%sp # stack 71# 72# Relocate ourself to a lower address so that we have more room to load 73# other sectors. 74# 75 movw $main-EXEC+LOAD,%si # Source 76 movw $main,%di # Destination 77 movw $SECSIZE-(main-start),%cx # Byte count 78 rep # Relocate 79 movsb # code 80# 81# Jump to the relocated code. 82# 83 jmp main-LOAD+EXEC # To relocated code 84# 85# Validate drive number in %dl. 86# 87main: cmpb $0x80,%dl # Drive valid? 88 jb main.1 # No 89 movb NHRDRV,%dh # Calculate the highest 90 addb $0x80,%dh # drive number available 91 cmpb %dh,%dl # Within range? 92 jb main.2 # Yes 93main.1: movb $0x80,%dl # Assume drive 0x80 94# 95# Load the GPT header and verify signature. Try LBA 1 for the primary one and 96# the last LBA for the backup if it is broken. 97# 98main.2: call getdrvparams # Read drive parameters 99 movb $1,%dh # %dh := 1 (reading primary) 100main.2a: movw $GPT_ADDR,%bx 101 movw $lba,%si 102 call read # Read header and check GPT sig 103 cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG 104 jnz main.2b 105 cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4 106 jnz main.2b 107 jmp load_part 108main.2b: cmpb $1,%dh # Reading primary? 109 jne err_pt # If no - invalid table found 110# 111# Try alternative LBAs from the last sector for the GPT header. 112# 113main.3: movb $0,%dh # %dh := 0 (reading backup) 114 movw $DPBUF+DPBUF_SEC,%si # %si = last sector + 1 115 movw $lba,%di # %di = $lba 116main.3a: subl $1, (%si) # 0x0(%si) = last sec (0-31) 117 sbbl $0, 4(%si) 118 movw $4,%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 movw $0x10,%cx 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: addl $1,(%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 addl $1, 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