libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / bin / writembr / mbr.S
blob0312aa63a3bd1d3cfe7837bf9b4ca322edec8d06
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  */
94                         .set LOAD,0x7c00
95                         .set EXEC,0x600
96                         .set MAGIC,0xaa55
97                         .set HANDSHAKE,0x55aa
98                         .set SECSIZE,0x200
100                         /* data offsets */
101                         .set UUID,0x1a0
102                         .set DISKSIG,0x1b8
103                         .set PT_OFF,0x1be
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. */
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 */
122                         .set NHRDRV,0x475                               /* Number of hard drives */
124                         .globl start                                    /* Entry point */
125                         .code16
128  * Setup the segment registers for flat addressing and setup the stack.
129  */
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
148  * other sectors.
149  */
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.
156  */
157                         jmp $0,$main                                    /* Absolute address (far) jump */
160  * Will land here; we're now at %cs = 0x0000 and %ip a little above 0x0600
161  */
162 main:           sti                                                             /* Re-enable interrupts */
164 #ifdef VALIDATE_DRV
166  * Validate drive number in %dl. Certain BIOSes might not like it.
167  */
168 validate_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 */
175 validate_drv.1:
176                         movb $0x80,%dl                                  /* Else, assume drive 0x80 */
177 #endif
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  */
184 test_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 */
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 */
197 test_magic:
198                         cmpw $MAGIC,%bx                                 /* Magic ok? */
199                         jne set_chs                                             /* No, fall back to CHS read */
200 test_packet:
201                         testb $0x1,%cl                                  /* Packet mode present? */
202                         jnz load_gpt_hdr                                /* Yes! */
203 set_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
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  */
214 load_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 */
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  */
229 load_part:
230                         movw $GPT_ADDR+GPT_PART_LBA,%si
231                         movw $PART_ADDR,%bx
232                         call read
233 scan:
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 */
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
248 load_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 */
260 next_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
271  * Move to the next partition. If we walk off the end of the sector, load
272  * the next sector.
273  */
274 next_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
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  */
289 try_mbr:
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) */
297 read_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 */
304 next_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 */
309 try_diskless:
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.
316  */
317 read_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 */
321 test_flag:
322                         cmpb $1,%dh                                             /* Test flag set by set_chs above */
323                         jz read_chs                                             /* CHS read if set */
324 read_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 */
332 read_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 */
338 finished_read:
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.
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 */
351 start_loader:
352                         xorw %ax,%ax
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.
363  */
364 read:
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
380  * Output an ASCIZ string pointed at by %si to the console via the BIOS.
381  */
382 putstr.0:
383                         movw $0x7,%bx                                   /* Page:attribute */
384                         movb $0xe,%ah                                   /* BIOS: Display */
385                         int $0x10                                               /*  character */
386 putstr:
387                         lodsb                                                   /* Get character */
388                         testb %al,%al                                   /* End of string? */
389                         jnz putstr.0                                    /* No */
390                         ret
394  * Various error message entry points.
395  */
396 err_part_table:
397                         movw $msg_badtable,%si                  /* "Bad Part. Table!" */
398                         call putstr
399                         jmp halt
402 err_reading:
403                         movw $msg_ioerror,%si                   /* "Read Error!" */
404                         call putstr
405                         jmp halt
408 err_noboot:
409                         movw $msg_noloader,%si                  /* "No Sys Loader!" */
410                         call putstr
411                         /* fall-through to halt */
414 halt:
415                         cli
416                         hlt
417                         jmp halt
420 /* Data section */
423 #ifdef VALIDATE_DRV
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!"
428 #else
429 msg_badtable:   .asciz "Bad Part. Table!"
430 msg_ioerror:    .asciz "Read Error!"
431 msg_noloader:   .asciz "No Sys Loader!"
432 #endif
434 /* Boot partition UUID signature */
435                         .org UUID,0x0                                   /* Zero-pad up to UUID offset */
437 boot_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                                              /* '!' */
450 #ifndef MBR_CODE_ONLY
451 /* Disk signature */
452                         .org DISKSIG,0x0                                /* Zero-pad up to signature offset */
454 sig:
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 */
461 partbl:
462                         .fill 0x10,0x4,0x0                              /* Partition table */
463                         .word MAGIC                                             /* Magic number */
464 #endif