3 * $FreeBSD: src/sys/boot/i386/pmbr/pmbr.s,v 1.2 2007/11/26 21:29:59 jhb Exp $
5 * Copyright (c) 2007 Yahoo!, Inc.
7 * Written by: John Baldwin <jhb@FreeBSD.org>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
35 * $FreeBSD: src/sys/boot/i386/mbr/mbr.s,v 1.7 2004/08/28 08:39:35 yar Exp $
37 * Copyright (c) 1999 Robert Nordier
38 * All rights reserved.
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
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
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
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
105 .set GPTSTACK,EXEC+SECSIZE*4-8 /* Stack address */
106 .set LBABUF,GPTSTACK /* LBA address pointer buffer, */
107 /* 8 bytes long, after stack */
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. */
119 .set PART_START_LBA,32 /* Offs to 1st LBA on part entry */
120 .set PART_END_LBA,40 /* Offs to last LBA */
122 .set NHRDRV,0x475 /* Number of hard drives */
124 .globl start /* Entry point */
128 * Setup the segment registers for flat addressing and setup the stack.
130 start: cli /* Clear interrupts before relocation */
132 xorw %ax,%ax /* Zero */
133 movw %ax,%es /* Address */
134 movw %ax,%ds /* data */
136 movw %ax,%ss /* Set up */
137 movw $GPTSTACK,%sp /* stack */
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. */
147 * Relocate ourself to a lower address so that we have more room to load
150 reloc: cld /* String ops set to increment */
151 movw $SECSIZE,%cx /* Now we walk forward and relocate. */
152 rep movsb /* Tricky, but works great! */
155 * Jump to the relocated code.
157 jmp $0,$main /* Absolute address (far) jump */
160 * Will land here; we're now at %cs = 0x0000 and %ip a little above 0x0600
162 main: sti /* Re-enable interrupts */
166 * Validate drive number in %dl. Certain BIOSes might not like it.
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 */
176 movb $0x80,%dl /* Else, assume drive 0x80 */
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)
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 */
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 */
198 cmpw $MAGIC,%bx /* Magic ok? */
199 jne set_chs /* No, fall back to CHS read */
201 testb $0x1,%cl /* Packet mode present? */
202 jnz load_gpt_hdr /* Yes! */
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 */
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.
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) */
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 */
226 * GPT is valid. Load a partition table sector from disk and look for a
227 * partition matching the UUID found in boot_uuid.
230 movw $GPT_ADDR+GPT_PART_LBA,%si
234 movw %bx,%si /* Compare partition UUID */
235 movw $boot_uuid,%di /* with Haiku boot UUID */
236 movb $0x10,%cl /* (16 bytes) */
238 jnz next_part /* Didn't match, next partition */
241 * We found a partition. Load it into RAM starting at 0x7c00.
243 movw %bx,%di /* Save partition pointer in %di */
244 leaw PART_START_LBA(%di),%si
249 push %si /* Save %si */
251 pop %si /* Restore */
252 movl PART_END_LBA(%di),%eax /* See if this was the last LBA */
255 movl PART_END_LBA+4(%di),%eax
258 jmp start_loader /* Jump to boot code */
261 incl (%si) /* Next LBA */
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. :) */
271 * Move to the next partition. If we walk off the end of the sector, load
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? */
281 incl GPT_ADDR+GPT_PART_LBA /* Next sector */
282 adcl $0,GPT_ADDR+GPT_PART_LBA+4
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.
290 xorw %ax,%ax /* Zero */
291 movw %ax,%es /* extra segment */
292 movw $LOAD,%sp /* Reset stack */
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) */
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 */
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 */
310 int $0x18 /* Else, BIOS: Diskless boot */
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.
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 */
322 cmpb $1,%dh /* Test flag set by set_chs above */
323 jz read_chs /* CHS read if set */
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 */
331 jmp finished_read /* Skip the CHS setup */
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 */
339 jc err_reading /* If error */
343 * Now that we've loaded the bootstrap, check for the 0xaa55 signature. If it
344 * is present, execute the bootstrap we just loaded.
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 */
353 movw %ax,%es /* Reset %es to zero */
354 jmp $0,$LOAD /* Jump to boot code */
357 /* Auxiliary functions */
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.
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 */
380 * Output an ASCIZ string pointed at by %si to the console via the BIOS.
383 movw $0x7,%bx /* Page:attribute */
384 movb $0xe,%ah /* BIOS: Display */
385 int $0x10 /* character */
387 lodsb /* Get character */
388 testb %al,%al /* End of string? */
389 jnz putstr.0 /* No */
394 * Various error message entry points.
397 movw $msg_badtable,%si /* "Bad Part. Table!" */
403 movw $msg_ioerror,%si /* "Read Error!" */
409 movw $msg_noloader,%si /* "No Sys Loader!" */
411 /* fall-through to halt */
424 /* Messages must be shortened so the code fits 440 bytes */
425 msg_badtable: .asciz "BadPTbl!"
426 msg_ioerror: .asciz "IOErr!"
427 msg_noloader: .asciz "NoSysLdr!"
429 msg_badtable: .asciz "Bad Part. Table!"
430 msg_ioerror: .asciz "Read Error!"
431 msg_noloader: .asciz "No Sys Loader!"
434 /* Boot partition UUID signature */
435 .org UUID,0x0 /* Zero-pad up to UUID offset */
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) */
450 #ifndef MBR_CODE_ONLY
452 .org DISKSIG,0x0 /* Zero-pad up to signature offset */
455 .long 0 /* OS Disk Signature */
456 .word 0 /* "Unknown" in PMBR */
458 /* Partition table */
459 .org PT_OFF,0x0 /* Won't pad, just documenting */
462 .fill 0x10,0x4,0x0 /* Partition table */
463 .word MAGIC /* Magic number */