1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 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. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 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. 22 * 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. 34 */ 35 36/* 37 * x86 PC BIOS master boot code - GUID partition table format 38 */ 39 40/* Compile options: 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 44 * 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' 48 */ 49 50#ifdef COM_PORT 51#if COM_PORT < 4 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)) 54#else 55#define COM_PORT_VAL $COM_PORT 56#endif 57 58#if !defined(COM_FREQ) 59#define COM_FREQ 1843200 60#endif 61#endif 62 63#include <machine/asm.h> 64#include <sys/bootblock.h> 65 66#define BOOTADDR 0x7c00 67#define GPTBUFADDR 0xa000 /* GPT buffer (both header & array) */ 68 69/* 70 * GPT header structure, offsets 71 */ 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 */ 86 87/* 88 * GPT partition entry structure, offsets 89 */ 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 */ 96 97/* 98 * Default values of generic disk partitioning 99 */ 100#ifndef SECTOR_SIZE 101#define SECTOR_SIZE 512 /* 8192 bytes max */ 102#endif 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) 111#else 112#define GPTPTN_LBASZ (GPTPTN_SIZE / SECTOR_SIZE + 1) 113#endif 114#define GPT_LBASZ (GPTHDR_LBASZ + GPTPTN_LBASZ) 115 116/* 117 * Minimum and maximum drive number that is considered to be valid. 118 */ 119#define MINDRV 0x80 120#define MAXDRV 0x8f 121 122/* 123 * Error codes. Done this way to save space. 124 */ 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 */ 129 130#define set_err(err) movb $err, %al 131 132 .text 133 .code16 134/* 135 * Move ourselves out of the way first. 136 * (to the address we are linked at) 137 * and zero our bss 138 */ 139ENTRY(start) 140 xor %ax, %ax 141 mov %ax, %ss 142 movw $BOOTADDR, %sp 143 mov %ax, %es 144 mov %ax, %ds 145 movw $mbr, %di 146 mov $BOOTADDR + (mbr - start), %si 147 push %ax /* zero for %cs of lret */ 148 push %di 149 movw $(bss_start - mbr), %cx 150 rep 151 movsb /* relocate code (zero %cx on exit) */ 152 mov $(bss_end - bss_start + 511)/512, %ch 153 rep 154 stosw /* zero bss */ 155 lret /* Ensures %cs == 0 */ 156 157/* 158 * Sanity check the drive number passed by the BIOS. Some BIOSs may not 159 * do this and pass garbage. 160 */ 161mbr: 162 cmpb $MAXDRV, %dl /* relies on MINDRV being 0x80 */ 163 jle 1f 164 movb $MINDRV, %dl /* garbage in, boot disk 0 */ 1651: 166 push %dx /* save drive number */ 167 push %dx /* twice - for err_msg loop */ 168 169#if defined(COM_PORT) && defined(COM_BAUD) 170 mov $com_args, %si 171 mov $num_com_args, %cl /* %ch is zero from above */ 172 mov COM_PORT_VAL, %dx 1731: lodsw 174 add %ah, %dl 175 outb %dx 176 loop 1b 177#endif 178 179#ifndef NO_BANNER 180 mov $banner, %si 181 call message 182#endif 183 184/* 185 * Read and validate GUID partition tables 186 * 187 * Register use: 188 * %ax temp 189 * %bx temp 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 193 */ 194#ifndef NO_LBA_CHECK 195/* 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. 199 */ 200lba_check: 201 movw $0x55aa, %bx 202 movb $0x41, %ah 203 int $0x13 204 set_err(ERR_NO_LBA) 205 jc 1f /* no int13 extensions */ 206 cmpw $0xaa55, %bx 207 jnz 1f 208 testb $1, %cl 209 jnz gpt 2101: jmp err_msg 211#endif 212 213gpt: 214 /* 215 * Read GPT header 216 */ 217 movw $GPTBUFADDR, %bp /* start from primary GPT layout */ 218read_gpt: 219 call do_lba_read /* read GPT header */ 220 jc try_gpt2 221 222 /* 223 * Verify GPT header 224 */ 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 */ 228 repe 229 cmpsb /* compare */ 230 jne try_gpt2 /* if not equal - try GPT2 */ 231 232#ifndef NO_CRC_CHECK 233 mov %bp, %si /* verify CRC32 of the header... */ 234 mov $GPTHDR_SIZE, %di /* header boundary */ 235 add %bp, %di 236 xor %eax, %eax 237 xchgl %eax, GPTHDR_CRC_O(%bp) /* save and reset header CRC */ 238 call crc32 239 cmp %eax, %ebx /* is CRC correct? */ 240 jne try_gpt2 241#endif 242 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 246 add %bp, %di 247 movb $8, %cl /* LBA size */ 248 repe 249 cmpsb /* compare */ 250 jne try_gpt2 /* if not equal - try GPT2 */ 251#endif 252 253 /* 254 * All header checks passed - now verify GPT partitions array 255 */ 256#ifndef NO_CRC_CHECK 257 movl GPTHDR_PTNCRC_O(%bp), %eax /* original array checksum */ 258 push %bp /* save header pointer for try_gpt2 */ 259#endif 260 261 /* 262 * point %bp to GPT partitions array location 263 */ 264 cmp $GPTBUFADDR, %bp 265 je 1f 266 mov $GPTBUFADDR, %bp 267 jmp 2f 2681: mov $GPTBUFADDR + GPTPTN_LBASZ * SECTOR_SIZE, %bp 2692: 270 271#ifndef NO_CRC_CHECK 272 mov %bp, %si /* array location for CRC32 check */ 273 mov $GPTPTN_SIZE, %di /* array boundary */ 274 add %bp, %di 275 call crc32 276 cmp %eax, %ebx /* is CRC correct? */ 277 jne 1f /* if no - try GPT2 */ 278 pop %ax /* restore stack consistency */ 279#endif 280 jmp gpt_parse 281 282#ifndef NO_CRC_CHECK 2831: pop %bp /* restore after unsucc. array check */ 284#endif 285try_gpt2: 286 cmp $GPTBUFADDR, %bp /* is this GPT1? */ 287 set_err(ERR_INVPART) 288 jne err_msg /* if no - we just tried GPT2. Stop. */ 289 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 */ 294 set_err(ERR_NO_LBA) 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 */ 301 rep 302 movsb /* do get */ 303 subl $GPT_LBASZ, lba_sector /* calculate location of GPT2 */ 304 sbbl $0, lba_sector + 4 /* 64-bit LBA correction */ 305 306 movw $GPTBUFADDR + GPTPTN_LBASZ * SECTOR_SIZE, %bp 307 /* the GPT2 header location */ 308 jmp read_gpt /* try once again */ 309 310/* 311 * GPT header validation done. 312 * Now parse GPT partitions and try to boot from appropriate. 313 * Register use: 314 * %bx partition counter 315 * %bp partition entry pointer (already initialized on entry) 316 * %di partition entry moving pointer 317 * %si variables pointer 318 * %cx counter 319 */ 320 321gpt_parse: 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 */ 325do_gpt_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 */ 330 repe 331 cmpsb /* do compare */ 332 jne try_nextptn /* doesn't seem appropriate ptn */ 333 334 /* 335 * Read partition boot record 336 */ 337 mov $GPTPTN_SLBA_O, %si /* point %si to partition LBA */ 338 add %bp, %si 339 movw $lba_sector, %di 340 movb $8, %cl /* LBA size */ 341 rep 342 movsb /* set read pointer to LBA of PBR */ 343 call do_lba_read /* read PBR */ 344 jz try_nextptn 345 346 /* 347 * Check signature for valid bootcode and try to boot 348 */ 349 movb BOOTADDR, %al /* first byte non-zero */ 350 testb %al, %al 351 jz 1f 352 movw BOOTADDR + MBR_MAGIC_OFFSET, %ax 3531: cmp $MBR_MAGIC, %ax 354 jne try_nextptn 355do_boot: 356 pop %dx /* ... %dx - drive # */ 357 movw $lba_sector, %sp /* ... %ecx:%ebx - boot partition LBA */ 358 pop %ebx 359 pop %ecx 360 movl crc32_poly, %eax /* X86_MBR_GPT_MAGIC */ 361 jmp BOOTADDR 362 /* THE END */ 363 364try_nextptn: 365 addw $GPTHDR_PTNSZ, %bp /* move to next partition */ 366 dec %bx /* ptncounter-- */ 367 jnz do_gpt_parse 368 set_err(ERR_NOOS) /* no bootable partitions were found */ 369 /* jmp err_msg */ /* stop */ 370 371/* Something went wrong... 372 * Output error code, 373 * reset disk subsystem - needed after read failure, 374 * and wait for user key 375 */ 376err_msg: 377 movb %al, errcod 378 movw $errtxt, %si 379 call message 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. */ 3851: sti 386 hlt 387 jmp 1b 388 389/* 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. 393 */ 394 395#if defined(COM_PORT) && defined(COM_BAUD) 396message: 397 pusha 398message_1: 399 lodsb 400 test %al, %al 401 jz 3f 402 mov COM_PORT_VAL, %dx 403 outb %al, %dx 404 add $5, %dl 4052: inb %dx 406 test $0x40, %al 407 jz 2b 408 jmp message_1 4093: popa 410 ret 411#else 412#include <message.S> 413#endif 414 415#if 0 416#include <dump_eax.S> 417#endif 418 419#ifndef NO_CRC_CHECK 420/* 421 * The CRC32 calculation 422 * 423 * %si address of block to hash 424 * %di stop address 425 * %ax scratch (but restored after exit) 426 * %dx scratch (but restored after exit) 427 * %cx counter 428 * %ebx crc (returned) 429 */ 430crc32: 431 push %dx /* preserve drive number */ 432 push %ax /* preserve original CRC */ 433 434 xorl %ebx, %ebx 435 decl %ebx /* init value */ 4361: 437 lodsb /* load next message byte to %al */ 438 movb $8, %cl /* set bit counter */ 4392: 440 movb %al, %dl 441 xorb %bl, %dl /* xoring with previous result */ 442 shrl $1, %ebx 443 shrb $1, %al 444 testb $1, %dl 445 jz 3f 446crc32_poly = . + 3 /* gross, but saves a few bytes */ 447 xorl $0xedb88320, %ebx /* EFI CRC32 Polynomial */ 4483: 449 loop 2b /* loop over bits */ 450 cmp %di, %si /* do we reached end of message? */ 451 jne 1b 452 notl %ebx /* result correction */ 453 454 pop %ax 455 pop %dx 456 ret 457#endif 458 459do_lba_read: 460 movw $lba_dap, %si 461 movb $0x42, %ah 462 int $0x13 /* read */ 463 ret 464 465/* 466 * Data definition block 467 */ 468 469errtxt: .ascii "Error " /* runs into crlf if errcod set */ 470errcod: .byte 0 471crlf: .asciz "\r\n" 472 473#ifndef NO_BANNER 474banner: .asciz "NetBSD GPT\r\n" 475#endif 476 477#if defined(COM_PORT) && defined(COM_BAUD) 478#define COM_DIVISOR (((COM_FREQ / COM_BAUD) + 8) / 16) 479com_args: 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 */ 488num_com_args = (. - com_args)/2 489#endif 490 491/* 492 * Control block for int-13 LBA read - Disk Address Packet 493 */ 494lba_dap: 495 .byte 0x10 /* control block length */ 496 .byte 0 /* reserved */ 497lba_count: 498 .word GPT_LBASZ /* sector count */ 499lba_rbuff: 500 .word GPTBUFADDR /* offset in segment */ 501 .word 0 /* segment */ 502lba_sector: 503 .quad GPTHDR_BLKNO /* sector # goes here... */ 504 505efihdr_sign: 506 .ascii "EFI PART" /* GPT header signature */ 507 .long 0x00010000 /* GPT header revision */ 508sizeof_efihdr_sign = . - efihdr_sign 509 510/* 511 * Stuff from here on is overwritten by gpt/fdisk - the offset must not change 512 * 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) 515 */ 516mbr_space = bootptn_guid - . 517 518/* 519 * GUID of the bootable partition. Patchable area. 520 * Default GUID used by installer for safety checks. 521 */ 522 . = start + MBR_GPT_GUID_OFFSET 523bootptn_guid: 524 /* MBR_GPT_GUID_DEFAULT */ 525 .long 0xeee69d04 526 .word 0x02f4 527 .word 0x11e0 528 .byte 0x8f,0x5d 529 .byte 0x00,0xe0,0x81,0x52,0x9a,0x6b 530 531/* space for mbr_dsn */ 532 . = start + MBR_DSN_OFFSET 533 .long 0 534 535/* mbr_bootsel_magic */ 536 . = start + MBR_BS_MAGIC_OFFSET 537 .word 0 538 539/* mbr partition table */ 540 . = start + MBR_PART_OFFSET 541parttab: 542 .fill 0x40, 0x01, 0x00 543 544 . = start + MBR_MAGIC_OFFSET 545 .word MBR_MAGIC 546 547/* zeroed data space */ 548bss_off = 0 549bss_start = . 550#define BSS(name, size) name = bss_start + bss_off; bss_off = bss_off + size 551 BSS(dump_eax_buff, 16) 552 BSS(bss_end, 0) 553