1/* 2 * Partly from: 3 * $FreeBSD: src/sys/boot/i386/pmbr/pmbr.s,v 1.2 2007/11/26 21:29:59 jhb Exp $ 4 * 5 * Copyright (c) 2007 Yahoo!, Inc. 6 * All rights reserved. 7 * Written by: John Baldwin <jhb@FreeBSD.org> 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the author nor the names of any co-contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * 34 * Partly from: 35 * $FreeBSD: src/sys/boot/i386/mbr/mbr.s,v 1.7 2004/08/28 08:39:35 yar Exp $ 36 * 37 * Copyright (c) 1999 Robert Nordier 38 * All rights reserved. 39 * 40 * Redistribution and use in source and binary forms are freely 41 * permitted provided that the above copyright notice and this 42 * paragraph and the following disclaimer are duplicated in all 43 * such forms. 44 * 45 * This software is provided "AS IS" and without any express or 46 * implied warranties, including, without limitation, the implied 47 * warranties of merchantability and fitness for a particular 48 * purpose. 49 * 50 * 51 * "Hybridisation" and modifications for booting Haiku by Andre' Braga 52 * (me@andrebraga.com), with valuable input from Jean-Loïc Charroud 53 * (jcharroud@free.fr). The modifications contained herein are released into 54 * the Public Domain. 55 * 56 * 57 * A 432 bytes MBR IPL (Initial Program Loader) that looks for the UUID of 58 * a Haiku Boot GUID partition and boots it, falling back to MBR partitions if 59 * said UUID isn't found or if the (primary) GPT is corrupted or non-existent. 60 * Its usefulness is in being a versatile, "universal" IPL that supports 61 * both partitioning styles, allowing it to be used transparently and even 62 * facilitating the conversion between partitioning styles, should the need 63 * for more partitions or volumes over 2TiB arise (for instance when cloning 64 * an older disk to a newer, more capacious one). 65 * It also paves the way for Haiku to create and support booting from 66 * multiple volumes larger than 2TiB, which we're in the very privileged 67 * position of enjoying efficiently in the near future due to BFS. Another use 68 * case is taking a disk from a Intel EFI machine, plugging it on a BIOS 69 * machine and boot just fine; and vice-versa. 70 * As mentioned, if there are valid partitions defined in the MBR, and the 71 * primary GPT becomes corrupt, it can fall back to loading the MBR partition 72 * with the active flag set, if one is defined. 73 * Currently there's no provision for falling back to the GPT copy that 74 * lives in the end of the disk, due to the 512 bytes constraint; supporting 75 * this is unlikely given that the code is packed tight. An alternative would be 76 * disabling support for booting from MBR using BIOS calls other than Int13h 77 * function 42h, "Extended Read From Disk" (i.e., LBA mode). It's unlikely that 78 * any machine that Haiku supports won't have this BIOS function, but having an 79 * "universal" IPL should be quite useful to, say, people using Haiku to 80 * rewrite a corrupt MBR on another disk using the excellent DiskProbe. 81 * The number of sectors loaded depends on the boot style. Booting from a 82 * MBR partition assumes that the Partition Boot Record is one sector long, 83 * whereas booting from a GPT partition assumes a partition exclusive for a 84 * system loader and will either copy its entirety into memory starting from 85 * address 0x7c00, or will copy up to circa 545KiB, whichever is smaller. Thus, 86 * it remains compatible with the FreeBSD gptloader and should work for loading 87 * Bootman from an exclusive Haiku boot manager partition as well. 88 * It should be easy to adjust the UUID signature as needed. It lives at 89 * offset 0x1a0 (416), leaving plenty of space before the 32-bit disk signature 90 * at offset 0x1b8 (440), so compatibility with Microsoft Windows and other OSs 91 * is maintained. 92 */ 93 94 .set LOAD,0x7c00 95 .set EXEC,0x600 96 .set MAGIC,0xaa55 97 .set HANDSHAKE,0x55aa 98 .set SECSIZE,0x200 99 100 /* data offsets */ 101 .set UUID,0x1a0 102 .set DISKSIG,0x1b8 103 .set PT_OFF,0x1be 104 105 .set GPTSTACK,EXEC+SECSIZE*4-8 /* Stack address */ 106 .set LBABUF,GPTSTACK /* LBA address pointer buffer, */ 107 /* 8 bytes long, after stack */ 108 109 .set GPT_ADDR,LBABUF+8 /* GPT header address */ 110 .set GPT_SIG,0 /* Signature offset from LBA 1 */ 111 .set GPT_SIG_0,0x20494645 /* "EFI ", bswapped */ 112 .set GPT_SIG_1,0x54524150 /* "PART", bswapped */ 113 .set GPT_MYLBA,24 /* Offs of curr header copy addr */ 114 .set GPT_PART_LBA,72 /* Offs of partitions start LBA */ 115 .set GPT_NPART,80 /* Offs to number of partitions */ 116 .set GPT_PART_SIZE,84 /* Offs to size of partition */ 117 .set PART_ADDR,GPT_ADDR+SECSIZE /* GPT partition array addr. */ 118 .set PART_TYPE,0 119 .set PART_START_LBA,32 /* Offs to 1st LBA on part entry */ 120 .set PART_END_LBA,40 /* Offs to last LBA */ 121 122 .set NHRDRV,0x475 /* Number of hard drives */ 123 124 .globl start /* Entry point */ 125 .code16 126 127/* 128 * Setup the segment registers for flat addressing and setup the stack. 129 */ 130start: cli /* Clear interrupts before relocation */ 131 132 xorw %ax,%ax /* Zero */ 133 movw %ax,%es /* Address */ 134 movw %ax,%ds /* data */ 135 136 movw %ax,%ss /* Set up */ 137 movw $GPTSTACK,%sp /* stack */ 138 139 std /* String ops set to decrement */ 140 movw $LOAD,%si /* We'll clear working memory starting */ 141 leaw -1(%si),%di /* from $LOAD-1 and stopping at EXEC. */ 142 movw $(LOAD-EXEC-1),%cx /* In the end we have %si pointing */ 143 rep stosb /* at LOAD and %di at EXEC. */ 144 145 146/* 147 * Relocate ourself to a lower address so that we have more room to load 148 * other sectors. 149 */ 150reloc: cld /* String ops set to increment */ 151 movw $SECSIZE,%cx /* Now we walk forward and relocate. */ 152 rep movsb /* Tricky, but works great! */ 153 154/* 155 * Jump to the relocated code. 156 */ 157 jmp $0,$main /* Absolute address (far) jump */ 158 159/* 160 * Will land here; we're now at %cs = 0x0000 and %ip a little above 0x0600 161 */ 162main: sti /* Re-enable interrupts */ 163 164#ifdef VALIDATE_DRV 165/* 166 * Validate drive number in %dl. Certain BIOSes might not like it. 167 */ 168validate_drv: 169 cmpb $0x80,%dl /* Drive valid? (>= 0x80) */ 170 jb validate_drv.1 /* No */ 171 movb NHRDRV,%dh /* Calculate the highest */ 172 addb $0x80,%dh /* drive number available */ 173 cmpb %dh,%dl /* Within range? */ 174 jb test_extensions /* Yes, proceed */ 175validate_drv.1: 176 movb $0x80,%dl /* Else, assume drive 0x80 */ 177#endif 178 179/* 180 * Test if BIOS supports Int13h extensions. If so, will try GPT scheme first. 181 * Else, sets flag (%dh = 1) and goes straight to MBR code. 182 * (%dl still contains the drive number from BIOS bootstrap) 183 */ 184test_extensions: 185 movb $0,%dh /* We'll test for EDD extensions. */ 186 /* LBA read (Int13,42) uses only */ 187 /* %dl to get drive number and if */ 188 /* we must fall back to CHS read */ 189 /* (Int13,02), %dh receives head */ 190 /* number, so it's clear to use */ 191 /* %dh to hold a "use CHS" flag */ 192 193 movw $HANDSHAKE,%bx /* EDD extensions magic number */ 194 movb $0x41,%ah /* BIOS: EDD extensions */ 195 int $0x13 /* present? */ 196 jc set_chs /* No, fall back to CHS read */ 197test_magic: 198 cmpw $MAGIC,%bx /* Magic ok? */ 199 jne set_chs /* No, fall back to CHS read */ 200test_packet: 201 testb $0x1,%cl /* Packet mode present? */ 202 jnz load_gpt_hdr /* Yes! */ 203set_chs: 204 movb $1,%dh /* read_chs overwrites this, and */ 205 /* Int13,42 only uses %dl, so */ 206 /* it's clear to use %dh as flag */ 207 jmp try_mbr 208 209 210/* 211 * If we reached here, drive is valid, LBA reads are available, will try GPT. 212 * Load the primary GPT header from LBA 1 and verify signature. 213 */ 214load_gpt_hdr: 215 movw $GPT_ADDR,%bx 216 movw $LBABUF,%si /* Will load LBA sector 1 from disk */ 217 movb $1,(%si) /* (64-bit value! Memory was zeroed so */ 218 /* it's OK to write only the LSB) */ 219 call read 220 cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG 221 jnz try_mbr /* If invalid GPT header */ 222 cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4 223 jnz try_mbr /* Fluke :( Try MBR now */ 224 225/* 226 * GPT is valid. Load a partition table sector from disk and look for a 227 * partition matching the UUID found in boot_uuid. 228 */ 229load_part: 230 movw $GPT_ADDR+GPT_PART_LBA,%si 231 movw $PART_ADDR,%bx 232 call read 233scan: 234 movw %bx,%si /* Compare partition UUID */ 235 movw $boot_uuid,%di /* with Haiku boot UUID */ 236 movb $0x10,%cl /* (16 bytes) */ 237 repe cmpsb 238 jnz next_part /* Didn't match, next partition */ 239 240/* 241 * We found a partition. Load it into RAM starting at 0x7c00. 242 */ 243 movw %bx,%di /* Save partition pointer in %di */ 244 leaw PART_START_LBA(%di),%si 245 movw $LOAD/16,%bx 246 movw %bx,%es 247 xorw %bx,%bx 248load_bootcode: 249 push %si /* Save %si */ 250 call read 251 pop %si /* Restore */ 252 movl PART_END_LBA(%di),%eax /* See if this was the last LBA */ 253 cmpl (%si),%eax 254 jnz next_boot_lba 255 movl PART_END_LBA+4(%di),%eax 256 cmpl 4(%si),%eax 257 jnz next_boot_lba 258 jmp start_loader /* Jump to boot code */ 259 260next_boot_lba: 261 incl (%si) /* Next LBA */ 262 adcl $0,4(%si) 263 mov %es,%ax /* Adjust segment for next */ 264 addw $SECSIZE/16,%ax /* sector */ 265 cmp $0x9000,%ax /* Don't load past 0x90000, */ 266 jae start_loader /* 545k should be enough for */ 267 mov %ax,%es /* any boot code. :) */ 268 jmp load_bootcode 269 270/* 271 * Move to the next partition. If we walk off the end of the sector, load 272 * the next sector. 273 */ 274next_part: 275 decl GPT_ADDR+GPT_NPART /* Was this the last partition? */ 276 jz try_mbr /* UUID boot signature not found */ 277 movw GPT_ADDR+GPT_PART_SIZE,%ax 278 addw %ax,%bx /* Next partition */ 279 cmpw $PART_ADDR+0x200,%bx /* Still in sector? */ 280 jb scan 281 incl GPT_ADDR+GPT_PART_LBA /* Next sector */ 282 adcl $0,GPT_ADDR+GPT_PART_LBA+4 283 jmp load_part 284 285/* 286 * If loading boot sectors from a GPT partition fails, try booting a MBR part. 287 * Reset stack/segment. Could have been tainted by the GPT loading code. 288 */ 289try_mbr: 290 xorw %ax,%ax /* Zero */ 291 movw %ax,%es /* extra segment */ 292 movw $LOAD,%sp /* Reset stack */ 293 294 xorw %si,%si /* Will point to active partition */ 295 movw $(EXEC+PT_OFF),%bx /* Point to partition table start */ 296 movw $0x4,%cx /* Tested entries counter (4 total) */ 297read_mbr_entry: 298 cmpb %ch,(%bx) /* Null entry? (%ch just zeroed) */ 299 je next_mbr_entry /* Yes */ 300 jg err_part_table /* If 0x1..0x7f */ 301 testw %si,%si /* Active already found? */ 302 jnz err_part_table /* Yes */ 303 movw %bx,%si /* Point to active */ 304next_mbr_entry: 305 addb $0x10,%bl /* Till */ 306 loop read_mbr_entry /* done */ 307 testw %si,%si /* Active found? */ 308 jnz read_bootsect /* Yes, read OS loader */ 309try_diskless: 310 int $0x18 /* Else, BIOS: Diskless boot */ 311 312 313/* 314 * Ok, now that we have a valid drive and partition entry, load either CHS 315 * or LBA from the partition entry and read the boot sector from the partition. 316 */ 317read_bootsect: 318 movw %sp,%bx /* Write addr. (%sp points to LOAD) */ 319 pushw %si /* Points at active part. entry; */ 320 /* save, else 'read' will trash it */ 321test_flag: 322 cmpb $1,%dh /* Test flag set by set_chs above */ 323 jz read_chs /* CHS read if set */ 324read_lba: 325 addw $0x8,%si /* Start LBA of partition, 32-bit */ 326 movw $LBABUF,%di /* So far either QWORD 1 or 0, so */ 327 movsl /* more significant bytes are all 0 */ 328 xchg %di,%si /* Copy to buffer and swap pointers */ 329 subw $0x4,%si /* Adjust back to start of buffer */ 330 call read 331 jmp finished_read /* Skip the CHS setup */ 332read_chs: 333 movb 0x1(%si),%dh /* Load head */ 334 movw 0x2(%si),%cx /* Load cylinder:sector */ 335 movb $2,%al /* Read two sectors */ 336 movb $2,%ah /* BIOS: Read sectors from disk */ 337 int $0x13 /* Call the BIOS */ 338finished_read: 339 jc err_reading /* If error */ 340 341 342/* 343 * Now that we've loaded the bootstrap, check for the 0xaa55 signature. If it 344 * is present, execute the bootstrap we just loaded. 345 */ 346 popw %si /* Restore %si (active part entry) */ 347 movb %dl,(%si) /* Patch part record with drive num */ 348 cmpw $MAGIC,0x1fe(%bx) /* Bootable? */ 349 jne err_noboot /* No, error out. */ 350 /* Else, start loader */ 351start_loader: 352 xorw %ax,%ax 353 movw %ax,%es /* Reset %es to zero */ 354 jmp $0,$LOAD /* Jump to boot code */ 355 356 357/* Auxiliary functions */ 358 359 360/* 361 * Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating 362 * a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si. 363 */ 364read: 365 pushl 0x4(%si) /* Set the LBA */ 366 pushl 0x0(%si) /* address */ 367 pushw %es /* Set the address of */ 368 pushw %bx /* the transfer buffer */ 369 pushw $0x1 /* Read 1 sector */ 370 pushw $0x10 /* Packet length */ 371 movw %sp,%si /* Packet pointer */ 372 movw $0x4200,%ax /* BIOS: LBA Read from disk */ 373 int $0x13 /* Call the BIOS */ 374 add $0x10,%sp /* Restore stack */ 375 jc err_reading /* If error */ 376 ret 377 378 379/* 380 * Output an ASCIZ string pointed at by %si to the console via the BIOS. 381 */ 382putstr.0: 383 movw $0x7,%bx /* Page:attribute */ 384 movb $0xe,%ah /* BIOS: Display */ 385 int $0x10 /* character */ 386putstr: 387 lodsb /* Get character */ 388 testb %al,%al /* End of string? */ 389 jnz putstr.0 /* No */ 390 ret 391 392 393/* 394 * Various error message entry points. 395 */ 396err_part_table: 397 movw $msg_badtable,%si /* "Bad Part. Table!" */ 398 call putstr 399 jmp halt 400 401 402err_reading: 403 movw $msg_ioerror,%si /* "Read Error!" */ 404 call putstr 405 jmp halt 406 407 408err_noboot: 409 movw $msg_noloader,%si /* "No Sys Loader!" */ 410 call putstr 411 /* fall-through to halt */ 412 413 414halt: 415 cli 416 hlt 417 jmp halt 418 419 420/* Data section */ 421 422 423#ifdef VALIDATE_DRV 424/* Messages must be shortened so the code fits 440 bytes */ 425msg_badtable: .asciz "BadPTbl!" 426msg_ioerror: .asciz "IOErr!" 427msg_noloader: .asciz "NoSysLdr!" 428#else 429msg_badtable: .asciz "Bad Part. Table!" 430msg_ioerror: .asciz "Read Error!" 431msg_noloader: .asciz "No Sys Loader!" 432#endif 433 434/* Boot partition UUID signature */ 435 .org UUID,0x0 /* Zero-pad up to UUID offset */ 436 437boot_uuid: 438 .long 0x42465331 /* 'BFS1' (formally, UUID time-low) */ 439 .word 0x3ba3 /* UUID time-mid */ 440 .word 0x10f1 /* UUID time-high & version (v1) */ 441 .byte 0x80 /* UUID DCE 1.1 variant */ 442 .byte 0x2a /* '*' (formally, UUID clock-seq-low) */ 443 .byte 0x48 /* 'H' */ 444 .byte 0x61 /* 'a' */ 445 .byte 0x69 /* 'i' */ 446 .byte 0x6b /* 'k' */ 447 .byte 0x75 /* 'u' */ 448 .byte 0x21 /* '!' */ 449 450#ifndef MBR_CODE_ONLY 451/* Disk signature */ 452 .org DISKSIG,0x0 /* Zero-pad up to signature offset */ 453 454sig: 455 .long 0 /* OS Disk Signature */ 456 .word 0 /* "Unknown" in PMBR */ 457 458/* Partition table */ 459 .org PT_OFF,0x0 /* Won't pad, just documenting */ 460 461partbl: 462 .fill 0x10,0x4,0x0 /* Partition table */ 463 .word MAGIC /* Magic number */ 464#endif 465