1 /* $NetBSD: gpt.S,v 1.1 2011/01/06 01:08:49 jakllsch Exp $ */
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to the NetBSD Foundation
8 * by Mike M. Volokhov, based on the mbr.S code by Wolfgang Solfrank,
9 * Frank van der Linden, and David Laight.
10 * Development of this software was supported by the
11 * Google Summer of Code program.
12 * The GSoC project was mentored by Allen Briggs and Joerg Sonnenberger.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
37 * x86 PC BIOS master boot code - GUID partition table format
41 * COM_PORT - do serial io to specified port number
42 * 0..3 => bios port, otherwise actual io_addr
43 * COM_BAUD - initialise serial port baud rate
45 * NO_LBA_CHECK - no check if bios supports LBA reads
46 * NO_CRC_CHECK - disable crc checks for GPT
47 * NO_BANNER - do not output title line 'banner'
52 /* The first 4 items in the 40:xx segment are the serial port base addresses */
53 #define COM_PORT_VAL (0x400 + (COM_PORT * 2))
55 #define COM_PORT_VAL $COM_PORT
58 #if !defined(COM_FREQ)
59 #define COM_FREQ 1843200
63 #include <machine/asm.h>
64 #include <sys/bootblock.h>
66 #define BOOTADDR 0x7c00
67 #define GPTBUFADDR 0xa000 /* GPT buffer (both header & array) */
70 * GPT header structure, offsets
72 #define GPTHDR_SIG_O 0 /* header signature, 8 b */
73 #define GPTHDR_REV_O 8 /* GPT revision, 4 b */
74 #define GPTHDR_SIZE_O 12 /* header size, 4 b */
75 #define GPTHDR_CRC_O 16 /* header CRC32, 4 b */
76 #define GPTHDR_RSVD_O 20 /* reserved, 4 b */
77 #define GPTHDR_MYLBA_O 24 /* this header LBA, 8 b */
78 #define GPTHDR_ALTLBA_O 32 /* alternate header LBA, 8 b */
79 #define GPTHDR_SLBA_O 40 /* first usable LBA, 8 b */
80 #define GPTHDR_ELBA_O 48 /* last usable LBA, 8 b */
81 #define GPTHDR_GUID_O 56 /* disk GUID, 16 b */
82 #define GPTHDR_PTNLBA_O 72 /* ptn. entry LBA, 8 b */
83 #define GPTHDR_PTNN_O 80 /* number of ptns, 4 b */
84 #define GPTHDR_PTNSZ_O 84 /* partition size, 4 b */
85 #define GPTHDR_PTNCRC_O 88 /* ptn. array CRC32, 4 b */
88 * GPT partition entry structure, offsets
90 #define GPTPTN_TYPE_O 0 /* ptn. type GUID, 16 b */
91 #define GPTPTN_GUID_O 16 /* ptn. unique GUID, 16 b */
92 #define GPTPTN_SLBA_O 32 /* ptn. starting LBA, 8 b */
93 #define GPTPTN_ELBA_O 40 /* ptn. ending LBA, 8 b */
94 #define GPTPTN_ATTR_O 48 /* ptn. attributes, 8 b */
95 #define GPTPTN_NAME_O 56 /* ptn. name, UTF-16, 72 b */
98 * Default values of generic disk partitioning
101 #define SECTOR_SIZE 512 /* 8192 bytes max */
103 #define GPTHDR_BLKNO 1
104 #define GPTHDR_SIZE 92 /* size of GPT header */
105 #define GPTHDR_LBASZ 1 /* default LBAs for header */
106 #define GPTHDR_PTNSZ 128 /* size of a partition entry */
107 #define GPTHDR_PTNN_MIN 128 /* minimum number of ptns */
108 #define GPTPTN_SIZE (GPTHDR_PTNSZ * GPTHDR_PTNN_MIN)
109 #if GPTPTN_SIZE % SECTOR_SIZE == 0
110 #define GPTPTN_LBASZ (GPTPTN_SIZE / SECTOR_SIZE)
112 #define GPTPTN_LBASZ (GPTPTN_SIZE / SECTOR_SIZE + 1)
114 #define GPT_LBASZ (GPTHDR_LBASZ + GPTPTN_LBASZ)
117 * Minimum and maximum drive number that is considered to be valid.
123 * Error codes. Done this way to save space.
125 #define ERR_INVPART 'P' /* Invalid partition table */
126 #define ERR_READ 'R' /* Read error */
127 #define ERR_NOOS 'S' /* Magic no. check failed for part. */
128 #define ERR_NO_LBA 'L' /* Sector above chs limit */
130 #define set_err(err) movb $err, %al
135 * Move ourselves out of the way first.
136 * (to the address we are linked at)
146 mov $BOOTADDR + (mbr - start), %si
147 push %ax /* zero for %cs of lret */
149 movw $(bss_start - mbr), %cx
151 movsb /* relocate code (zero %cx on exit) */
152 mov $(bss_end - bss_start + 511)/512, %ch
155 lret /* Ensures %cs == 0 */
158 * Sanity check the drive number passed by the BIOS. Some BIOSs may not
159 * do this and pass garbage.
162 cmpb $MAXDRV, %dl /* relies on MINDRV being 0x80 */
164 movb $MINDRV, %dl /* garbage in, boot disk 0 */
166 push %dx /* save drive number */
167 push %dx /* twice - for err_msg loop */
169 #if defined(COM_PORT) && defined(COM_BAUD)
171 mov $num_com_args, %cl /* %ch is zero from above */
172 mov COM_PORT_VAL, %dx
185 * Read and validate GUID partition tables
190 * %cx counters (%ch was preset to zero via %cx before)
191 * %dx disk pointer (inherited from BIOS or the check above)
192 * %bp GPT header pointer
196 * Determine whether we have int13-extensions, by calling int 13, function 41.
197 * Check for the magic number returned, and the disk packet capability.
198 * On entry %dx should contain BIOS disk number.
205 jc 1f /* no int13 extensions */
217 movw $GPTBUFADDR, %bp /* start from primary GPT layout */
219 call do_lba_read /* read GPT header */
225 movw $efihdr_sign, %si /* verify GPT signature... */
226 movw %bp, %di /* ptn signature is at offset 0 */
227 movb $sizeof_efihdr_sign, %cl /* signature length */
230 jne try_gpt2 /* if not equal - try GPT2 */
233 mov %bp, %si /* verify CRC32 of the header... */
234 mov $GPTHDR_SIZE, %di /* header boundary */
237 xchgl %eax, GPTHDR_CRC_O(%bp) /* save and reset header CRC */
239 cmp %eax, %ebx /* is CRC correct? */
243 #if 0 /* XXX: weak check - safely disabled due to space constraints */
244 movw $lba_sector, %si /* verify GPT location... */
245 mov $GPTHDR_MYLBA_O, %di
247 movb $8, %cl /* LBA size */
250 jne try_gpt2 /* if not equal - try GPT2 */
254 * All header checks passed - now verify GPT partitions array
257 movl GPTHDR_PTNCRC_O(%bp), %eax /* original array checksum */
258 push %bp /* save header pointer for try_gpt2 */
262 * point %bp to GPT partitions array location
268 1: mov $GPTBUFADDR + GPTPTN_LBASZ * SECTOR_SIZE, %bp
272 mov %bp, %si /* array location for CRC32 check */
273 mov $GPTPTN_SIZE, %di /* array boundary */
276 cmp %eax, %ebx /* is CRC correct? */
277 jne 1f /* if no - try GPT2 */
278 pop %ax /* restore stack consistency */
283 1: pop %bp /* restore after unsucc. array check */
286 cmp $GPTBUFADDR, %bp /* is this GPT1? */
288 jne err_msg /* if no - we just tried GPT2. Stop. */
290 mov %bp, %si /* use [%bp] as tmp buffer */
291 movb $0x1a, (%si) /* init buffer size (per v.1.0) */
292 movb $0x48, %ah /* request extended LBA status */
293 int $0x13 /* ... to get GPT2 location */
295 jc err_msg /* on error - stop */
296 #define LBA_DKINFO_OFFSET 16 /* interested offset in out buffer */
297 addw $LBA_DKINFO_OFFSET, %si /* ... contains number of disk LBAs */
298 #undef LBA_DKINFO_OFFSET
299 movw $lba_sector, %di
300 movb $8, %cl /* LBA size */
303 subl $GPT_LBASZ, lba_sector /* calculate location of GPT2 */
304 sbbl $0, lba_sector + 4 /* 64-bit LBA correction */
306 movw $GPTBUFADDR + GPTPTN_LBASZ * SECTOR_SIZE, %bp
307 /* the GPT2 header location */
308 jmp read_gpt /* try once again */
311 * GPT header validation done.
312 * Now parse GPT partitions and try to boot from appropriate.
314 * %bx partition counter
315 * %bp partition entry pointer (already initialized on entry)
316 * %di partition entry moving pointer
317 * %si variables pointer
322 movw $BOOTADDR, lba_rbuff /* from now we will read boot code */
323 movb $1, lba_count /* read PBR only */
324 mov $GPTHDR_PTNN_MIN, %bx /* number of GUID partitions to parse */
326 movw $bootptn_guid, %si /* lookup the boot partition GUID */
327 movb $0x10, %cl /* sizeof GUID */
328 movw %bp, %di /* set pointer to partition entry */
329 add %cx, %di /* partition GUID at offset 16 */
331 cmpsb /* do compare */
332 jne try_nextptn /* doesn't seem appropriate ptn */
335 * Read partition boot record
337 mov $GPTPTN_SLBA_O, %si /* point %si to partition LBA */
339 movw $lba_sector, %di
340 movb $8, %cl /* LBA size */
342 movsb /* set read pointer to LBA of PBR */
343 call do_lba_read /* read PBR */
347 * Check signature for valid bootcode and try to boot
349 movb BOOTADDR, %al /* first byte non-zero */
352 movw BOOTADDR + MBR_MAGIC_OFFSET, %ax
353 1: cmp $MBR_MAGIC, %ax
356 pop %dx /* ... %dx - drive # */
357 movw $lba_sector, %sp /* ... %ecx:%ebx - boot partition LBA */
360 movl crc32_poly, %eax /* X86_MBR_GPT_MAGIC */
365 addw $GPTHDR_PTNSZ, %bp /* move to next partition */
366 dec %bx /* ptncounter-- */
368 set_err(ERR_NOOS) /* no bootable partitions were found */
369 /* jmp err_msg */ /* stop */
371 /* Something went wrong...
373 * reset disk subsystem - needed after read failure,
374 * and wait for user key
380 pop %dx /* drive we errored on */
381 xor %ax,%ax /* only need %ah = 0 */
382 int $0x13 /* reset disk subsystem */
383 int $0x18 /* BIOS might ask for a key */
384 /* press and retry boot seq. */
390 * I hate #including source files, but the stuff below has to be at
391 * the correct absolute address.
392 * Clearly this could be done with a linker script.
395 #if defined(COM_PORT) && defined(COM_BAUD)
402 mov COM_PORT_VAL, %dx
416 #include <dump_eax.S>
421 * The CRC32 calculation
423 * %si address of block to hash
425 * %ax scratch (but restored after exit)
426 * %dx scratch (but restored after exit)
428 * %ebx crc (returned)
431 push %dx /* preserve drive number */
432 push %ax /* preserve original CRC */
435 decl %ebx /* init value */
437 lodsb /* load next message byte to %al */
438 movb $8, %cl /* set bit counter */
441 xorb %bl, %dl /* xoring with previous result */
446 crc32_poly = . + 3 /* gross, but saves a few bytes */
447 xorl $0xedb88320, %ebx /* EFI CRC32 Polynomial */
449 loop 2b /* loop over bits */
450 cmp %di, %si /* do we reached end of message? */
452 notl %ebx /* result correction */
466 * Data definition block
469 errtxt: .ascii "Error " /* runs into crlf if errcod set */
474 banner: .asciz "NetBSD GPT\r\n"
477 #if defined(COM_PORT) && defined(COM_BAUD)
478 #define COM_DIVISOR (((COM_FREQ / COM_BAUD) + 8) / 16)
480 .byte 0x80 /* divisor latch enable */
481 .byte +3 /* io_port + 3 */
482 .byte COM_DIVISOR & 0xff
483 .byte -3 /* io_port */
484 .byte COM_DIVISOR >> 8 /* high baud */
485 .byte +1 /* io_port + 1 */
486 .byte 0x03 /* 8 bit no parity */
487 .byte +2 /* io_port + 3 */
488 num_com_args = (. - com_args)/2
492 * Control block for int-13 LBA read - Disk Address Packet
495 .byte 0x10 /* control block length */
496 .byte 0 /* reserved */
498 .word GPT_LBASZ /* sector count */
500 .word GPTBUFADDR /* offset in segment */
501 .word 0 /* segment */
503 .quad GPTHDR_BLKNO /* sector # goes here... */
506 .ascii "EFI PART" /* GPT header signature */
507 .long 0x00010000 /* GPT header revision */
508 sizeof_efihdr_sign = . - efihdr_sign
511 * Stuff from here on is overwritten by gpt/fdisk - the offset must not change
513 * Get amount of space to makefile can report it.
514 * (Unfortunately I can't seem to get the value reported when it is -ve)
516 mbr_space = bootptn_guid - .
519 * GUID of the bootable partition. Patchable area.
520 * Default GUID used by installer for safety checks.
522 . = start + MBR_GPT_GUID_OFFSET
524 /* MBR_GPT_GUID_DEFAULT */
529 .byte 0x00,0xe0,0x81,0x52,0x9a,0x6b
531 /* space for mbr_dsn */
532 . = start + MBR_DSN_OFFSET
535 /* mbr_bootsel_magic */
536 . = start + MBR_BS_MAGIC_OFFSET
539 /* mbr partition table */
540 . = start + MBR_PART_OFFSET
542 .fill 0x40, 0x01, 0x00
544 . = start + MBR_MAGIC_OFFSET
547 /* zeroed data space */
550 #define BSS(name, size) name = bss_start + bss_off; bss_off = bss_off + size
551 BSS(dump_eax_buff, 16)