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: subl $1, (%si) # 0x0(%si) = last sec (0-31) 118 sbbl $0, 4(%si) 119 movw $4,%cx 120 rep 121 movsw # $lastsec--, copy it to $lba 122 jmp main.2a # Read the next sector 123# 124# Load a partition table sector from disk and look for a FreeBSD boot 125# partition. 126# 127load_part: movw $GPT_ADDR+GPT_PART_LBA,%si 128 movw $PART_ADDR,%bx 129 call read 130scan: movw %bx,%si # Compare partition UUID 131 movw $boot_uuid,%di # with FreeBSD boot UUID 132 movw $0x10,%cx 133 repe cmpsb 134 jnz next_part # Didn't match, next partition 135# 136# We found a boot partition. Load it into RAM starting at 0x7c00. 137# 138 movw %bx,%di # Save partition pointer in %di 139 leaw PART_START_LBA(%di),%si 140 movw $LOAD/16,%bx 141 movw %bx,%es 142 xorw %bx,%bx 143load_boot: push %si # Save %si 144 call read 145 pop %si # Restore 146 movl PART_END_LBA(%di),%eax # See if this was the last LBA 147 cmpl (%si),%eax 148 jnz next_boot 149 movl PART_END_LBA+4(%di),%eax 150 cmpl 4(%si),%eax 151 jnz next_boot 152 mov %bx,%es # Reset %es to zero 153 jmp LOAD # Jump to boot code 154next_boot: addl $1,(%si) # Next LBA 155 adcl $0,4(%si) 156 mov %es,%ax # Adjust segment for next 157 addw $SECSIZE/16,%ax # sector 158 cmp $0x9000,%ax # Don't load past 0x90000, 159 jae err_big # 545k should be enough for 160 mov %ax,%es # any boot code. :) 161 jmp load_boot 162# 163# Move to the next partition. If we walk off the end of the sector, load 164# the next sector. We assume that partition entries are smaller than 64k 165# 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? 170 jz err_noboot 171 movw GPT_ADDR+GPT_PART_SIZE,%ax 172 addw %ax,%bx # Next partition 173 cmpw $PART_ADDR+0x200,%bx # Still in sector? 174 jb scan 175 addl $1, GPT_ADDR+GPT_PART_LBA # Next sector 176 adcl $0,GPT_ADDR+GPT_PART_LBA+4 177 jmp load_part 178# 179# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating 180# a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si. 181# 182read: pushl 0x4(%si) # Set the LBA 183 pushl 0x0(%si) # address 184 pushw %es # Set the address of 185 pushw %bx # the transfer buffer 186 pushw $0x1 # Read 1 sector 187 pushw $0x10 # Packet length 188 movw %sp,%si # Packer pointer 189 movw $0x4200,%ax # BIOS: LBA Read from disk 190 int $0x13 # Call the BIOS 191 add $0x10,%sp # Restore stack 192 jc err_rd # If error 193 ret 194# 195# Check the number of LBAs on the drive index %dx. Trashes %ax and %si. 196# 197getdrvparams: 198 movw $DPBUF,%si # Set the address of result buf 199 movw $0x001e,(%si) # len 200 movw $0x4800,%ax # BIOS: Read Drive Parameters 201 int $0x13 # Call the BIOS 202 jc err_rd # "I/O error" if error 203 ret 204# 205# Various error message entry points. 206# 207err_big: movw $msg_big,%si # "Boot loader too 208 jmp putstr # large" 209 210err_pt: movw $msg_pt,%si # "Invalid partition 211 jmp putstr # table" 212 213err_rd: movw $msg_rd,%si # "I/O error loading 214 jmp putstr # boot loader" 215 216err_noboot: movw $msg_noboot,%si # "Missing boot 217 jmp putstr # loader" 218# 219# Output an ASCIZ string to the console via the BIOS. 220# 221putstr.0: movw $0x7,%bx # Page:attribute 222 movb $0xe,%ah # BIOS: Display 223 int $0x10 # character 224putstr: lodsb # Get character 225 testb %al,%al # End of string? 226 jnz putstr.0 # No 227putstr.1: jmp putstr.1 # Await reset 228 229msg_big: .asciz "Boot loader too large" 230msg_pt: .asciz "Invalid partition table" 231msg_rd: .asciz "I/O error loading boot loader" 232msg_noboot: .asciz "Missing boot loader" 233 234lba: .quad 1 # LBA of GPT header 235 236boot_uuid: .long 0x83bd6b9d 237 .word 0x7f41 238 .word 0x11dc 239 .byte 0xbe 240 .byte 0x0b 241 .byte 0x00 242 .byte 0x15 243 .byte 0x60 244 .byte 0xb8 245 .byte 0x4f 246 .byte 0x0f 247 248 .org DISKSIG,0x90 249sig: .long 0 # OS Disk Signature 250 .word 0 # "Unknown" in PMBR 251 252partbl: .fill 0x10,0x4,0x0 # Partition table 253 .word MAGIC # Magic number 254