Ticket #4028: new_haiku_gpt_mbr.S

File new_haiku_gpt_mbr.S, 15.5 KB (added by meianoite, 13 years ago)

Fixed a handful of comments, a typo in code, moved try_diskless to a better location and gave due credit to Starsseed

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