minix/sys/arch/i386/stand/mbr/gpt.S
2012-02-09 18:54:39 +01:00

552 lines
14 KiB
ArmAsm

/* $NetBSD: gpt.S,v 1.1 2011/01/06 01:08:49 jakllsch Exp $ */
/*
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to the NetBSD Foundation
* by Mike M. Volokhov, based on the mbr.S code by Wolfgang Solfrank,
* Frank van der Linden, and David Laight.
* Development of this software was supported by the
* Google Summer of Code program.
* The GSoC project was mentored by Allen Briggs and Joerg Sonnenberger.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* x86 PC BIOS master boot code - GUID partition table format
*/
/* Compile options:
* COM_PORT - do serial io to specified port number
* 0..3 => bios port, otherwise actual io_addr
* COM_BAUD - initialise serial port baud rate
*
* NO_LBA_CHECK - no check if bios supports LBA reads
* NO_CRC_CHECK - disable crc checks for GPT
* NO_BANNER - do not output title line 'banner'
*/
#ifdef COM_PORT
#if COM_PORT < 4
/* The first 4 items in the 40:xx segment are the serial port base addresses */
#define COM_PORT_VAL (0x400 + (COM_PORT * 2))
#else
#define COM_PORT_VAL $COM_PORT
#endif
#if !defined(COM_FREQ)
#define COM_FREQ 1843200
#endif
#endif
#include <machine/asm.h>
#include <sys/bootblock.h>
#define BOOTADDR 0x7c00
#define GPTBUFADDR 0xa000 /* GPT buffer (both header & array) */
/*
* GPT header structure, offsets
*/
#define GPTHDR_SIG_O 0 /* header signature, 8 b */
#define GPTHDR_REV_O 8 /* GPT revision, 4 b */
#define GPTHDR_SIZE_O 12 /* header size, 4 b */
#define GPTHDR_CRC_O 16 /* header CRC32, 4 b */
#define GPTHDR_RSVD_O 20 /* reserved, 4 b */
#define GPTHDR_MYLBA_O 24 /* this header LBA, 8 b */
#define GPTHDR_ALTLBA_O 32 /* alternate header LBA, 8 b */
#define GPTHDR_SLBA_O 40 /* first usable LBA, 8 b */
#define GPTHDR_ELBA_O 48 /* last usable LBA, 8 b */
#define GPTHDR_GUID_O 56 /* disk GUID, 16 b */
#define GPTHDR_PTNLBA_O 72 /* ptn. entry LBA, 8 b */
#define GPTHDR_PTNN_O 80 /* number of ptns, 4 b */
#define GPTHDR_PTNSZ_O 84 /* partition size, 4 b */
#define GPTHDR_PTNCRC_O 88 /* ptn. array CRC32, 4 b */
/*
* GPT partition entry structure, offsets
*/
#define GPTPTN_TYPE_O 0 /* ptn. type GUID, 16 b */
#define GPTPTN_GUID_O 16 /* ptn. unique GUID, 16 b */
#define GPTPTN_SLBA_O 32 /* ptn. starting LBA, 8 b */
#define GPTPTN_ELBA_O 40 /* ptn. ending LBA, 8 b */
#define GPTPTN_ATTR_O 48 /* ptn. attributes, 8 b */
#define GPTPTN_NAME_O 56 /* ptn. name, UTF-16, 72 b */
/*
* Default values of generic disk partitioning
*/
#ifndef SECTOR_SIZE
#define SECTOR_SIZE 512 /* 8192 bytes max */
#endif
#define GPTHDR_BLKNO 1
#define GPTHDR_SIZE 92 /* size of GPT header */
#define GPTHDR_LBASZ 1 /* default LBAs for header */
#define GPTHDR_PTNSZ 128 /* size of a partition entry */
#define GPTHDR_PTNN_MIN 128 /* minimum number of ptns */
#define GPTPTN_SIZE (GPTHDR_PTNSZ * GPTHDR_PTNN_MIN)
#if GPTPTN_SIZE % SECTOR_SIZE == 0
#define GPTPTN_LBASZ (GPTPTN_SIZE / SECTOR_SIZE)
#else
#define GPTPTN_LBASZ (GPTPTN_SIZE / SECTOR_SIZE + 1)
#endif
#define GPT_LBASZ (GPTHDR_LBASZ + GPTPTN_LBASZ)
/*
* Minimum and maximum drive number that is considered to be valid.
*/
#define MINDRV 0x80
#define MAXDRV 0x8f
/*
* Error codes. Done this way to save space.
*/
#define ERR_INVPART 'P' /* Invalid partition table */
#define ERR_READ 'R' /* Read error */
#define ERR_NOOS 'S' /* Magic no. check failed for part. */
#define ERR_NO_LBA 'L' /* Sector above chs limit */
#define set_err(err) movb $err, %al
.text
.code16
/*
* Move ourselves out of the way first.
* (to the address we are linked at)
* and zero our bss
*/
ENTRY(start)
xor %ax, %ax
mov %ax, %ss
movw $BOOTADDR, %sp
mov %ax, %es
mov %ax, %ds
movw $mbr, %di
mov $BOOTADDR + (mbr - start), %si
push %ax /* zero for %cs of lret */
push %di
movw $(bss_start - mbr), %cx
rep
movsb /* relocate code (zero %cx on exit) */
mov $(bss_end - bss_start + 511)/512, %ch
rep
stosw /* zero bss */
lret /* Ensures %cs == 0 */
/*
* Sanity check the drive number passed by the BIOS. Some BIOSs may not
* do this and pass garbage.
*/
mbr:
cmpb $MAXDRV, %dl /* relies on MINDRV being 0x80 */
jle 1f
movb $MINDRV, %dl /* garbage in, boot disk 0 */
1:
push %dx /* save drive number */
push %dx /* twice - for err_msg loop */
#if defined(COM_PORT) && defined(COM_BAUD)
mov $com_args, %si
mov $num_com_args, %cl /* %ch is zero from above */
mov COM_PORT_VAL, %dx
1: lodsw
add %ah, %dl
outb %dx
loop 1b
#endif
#ifndef NO_BANNER
mov $banner, %si
call message
#endif
/*
* Read and validate GUID partition tables
*
* Register use:
* %ax temp
* %bx temp
* %cx counters (%ch was preset to zero via %cx before)
* %dx disk pointer (inherited from BIOS or the check above)
* %bp GPT header pointer
*/
#ifndef NO_LBA_CHECK
/*
* Determine whether we have int13-extensions, by calling int 13, function 41.
* Check for the magic number returned, and the disk packet capability.
* On entry %dx should contain BIOS disk number.
*/
lba_check:
movw $0x55aa, %bx
movb $0x41, %ah
int $0x13
set_err(ERR_NO_LBA)
jc 1f /* no int13 extensions */
cmpw $0xaa55, %bx
jnz 1f
testb $1, %cl
jnz gpt
1: jmp err_msg
#endif
gpt:
/*
* Read GPT header
*/
movw $GPTBUFADDR, %bp /* start from primary GPT layout */
read_gpt:
call do_lba_read /* read GPT header */
jc try_gpt2
/*
* Verify GPT header
*/
movw $efihdr_sign, %si /* verify GPT signature... */
movw %bp, %di /* ptn signature is at offset 0 */
movb $sizeof_efihdr_sign, %cl /* signature length */
repe
cmpsb /* compare */
jne try_gpt2 /* if not equal - try GPT2 */
#ifndef NO_CRC_CHECK
mov %bp, %si /* verify CRC32 of the header... */
mov $GPTHDR_SIZE, %di /* header boundary */
add %bp, %di
xor %eax, %eax
xchgl %eax, GPTHDR_CRC_O(%bp) /* save and reset header CRC */
call crc32
cmp %eax, %ebx /* is CRC correct? */
jne try_gpt2
#endif
#if 0 /* XXX: weak check - safely disabled due to space constraints */
movw $lba_sector, %si /* verify GPT location... */
mov $GPTHDR_MYLBA_O, %di
add %bp, %di
movb $8, %cl /* LBA size */
repe
cmpsb /* compare */
jne try_gpt2 /* if not equal - try GPT2 */
#endif
/*
* All header checks passed - now verify GPT partitions array
*/
#ifndef NO_CRC_CHECK
movl GPTHDR_PTNCRC_O(%bp), %eax /* original array checksum */
push %bp /* save header pointer for try_gpt2 */
#endif
/*
* point %bp to GPT partitions array location
*/
cmp $GPTBUFADDR, %bp
je 1f
mov $GPTBUFADDR, %bp
jmp 2f
1: mov $GPTBUFADDR + GPTPTN_LBASZ * SECTOR_SIZE, %bp
2:
#ifndef NO_CRC_CHECK
mov %bp, %si /* array location for CRC32 check */
mov $GPTPTN_SIZE, %di /* array boundary */
add %bp, %di
call crc32
cmp %eax, %ebx /* is CRC correct? */
jne 1f /* if no - try GPT2 */
pop %ax /* restore stack consistency */
#endif
jmp gpt_parse
#ifndef NO_CRC_CHECK
1: pop %bp /* restore after unsucc. array check */
#endif
try_gpt2:
cmp $GPTBUFADDR, %bp /* is this GPT1? */
set_err(ERR_INVPART)
jne err_msg /* if no - we just tried GPT2. Stop. */
mov %bp, %si /* use [%bp] as tmp buffer */
movb $0x1a, (%si) /* init buffer size (per v.1.0) */
movb $0x48, %ah /* request extended LBA status */
int $0x13 /* ... to get GPT2 location */
set_err(ERR_NO_LBA)
jc err_msg /* on error - stop */
#define LBA_DKINFO_OFFSET 16 /* interested offset in out buffer */
addw $LBA_DKINFO_OFFSET, %si /* ... contains number of disk LBAs */
#undef LBA_DKINFO_OFFSET
movw $lba_sector, %di
movb $8, %cl /* LBA size */
rep
movsb /* do get */
subl $GPT_LBASZ, lba_sector /* calculate location of GPT2 */
sbbl $0, lba_sector + 4 /* 64-bit LBA correction */
movw $GPTBUFADDR + GPTPTN_LBASZ * SECTOR_SIZE, %bp
/* the GPT2 header location */
jmp read_gpt /* try once again */
/*
* GPT header validation done.
* Now parse GPT partitions and try to boot from appropriate.
* Register use:
* %bx partition counter
* %bp partition entry pointer (already initialized on entry)
* %di partition entry moving pointer
* %si variables pointer
* %cx counter
*/
gpt_parse:
movw $BOOTADDR, lba_rbuff /* from now we will read boot code */
movb $1, lba_count /* read PBR only */
mov $GPTHDR_PTNN_MIN, %bx /* number of GUID partitions to parse */
do_gpt_parse:
movw $bootptn_guid, %si /* lookup the boot partition GUID */
movb $0x10, %cl /* sizeof GUID */
movw %bp, %di /* set pointer to partition entry */
add %cx, %di /* partition GUID at offset 16 */
repe
cmpsb /* do compare */
jne try_nextptn /* doesn't seem appropriate ptn */
/*
* Read partition boot record
*/
mov $GPTPTN_SLBA_O, %si /* point %si to partition LBA */
add %bp, %si
movw $lba_sector, %di
movb $8, %cl /* LBA size */
rep
movsb /* set read pointer to LBA of PBR */
call do_lba_read /* read PBR */
jz try_nextptn
/*
* Check signature for valid bootcode and try to boot
*/
movb BOOTADDR, %al /* first byte non-zero */
testb %al, %al
jz 1f
movw BOOTADDR + MBR_MAGIC_OFFSET, %ax
1: cmp $MBR_MAGIC, %ax
jne try_nextptn
do_boot:
pop %dx /* ... %dx - drive # */
movw $lba_sector, %sp /* ... %ecx:%ebx - boot partition LBA */
pop %ebx
pop %ecx
movl crc32_poly, %eax /* X86_MBR_GPT_MAGIC */
jmp BOOTADDR
/* THE END */
try_nextptn:
addw $GPTHDR_PTNSZ, %bp /* move to next partition */
dec %bx /* ptncounter-- */
jnz do_gpt_parse
set_err(ERR_NOOS) /* no bootable partitions were found */
/* jmp err_msg */ /* stop */
/* Something went wrong...
* Output error code,
* reset disk subsystem - needed after read failure,
* and wait for user key
*/
err_msg:
movb %al, errcod
movw $errtxt, %si
call message
pop %dx /* drive we errored on */
xor %ax,%ax /* only need %ah = 0 */
int $0x13 /* reset disk subsystem */
int $0x18 /* BIOS might ask for a key */
/* press and retry boot seq. */
1: sti
hlt
jmp 1b
/*
* I hate #including source files, but the stuff below has to be at
* the correct absolute address.
* Clearly this could be done with a linker script.
*/
#if defined(COM_PORT) && defined(COM_BAUD)
message:
pusha
message_1:
lodsb
test %al, %al
jz 3f
mov COM_PORT_VAL, %dx
outb %al, %dx
add $5, %dl
2: inb %dx
test $0x40, %al
jz 2b
jmp message_1
3: popa
ret
#else
#include <message.S>
#endif
#if 0
#include <dump_eax.S>
#endif
#ifndef NO_CRC_CHECK
/*
* The CRC32 calculation
*
* %si address of block to hash
* %di stop address
* %ax scratch (but restored after exit)
* %dx scratch (but restored after exit)
* %cx counter
* %ebx crc (returned)
*/
crc32:
push %dx /* preserve drive number */
push %ax /* preserve original CRC */
xorl %ebx, %ebx
decl %ebx /* init value */
1:
lodsb /* load next message byte to %al */
movb $8, %cl /* set bit counter */
2:
movb %al, %dl
xorb %bl, %dl /* xoring with previous result */
shrl $1, %ebx
shrb $1, %al
testb $1, %dl
jz 3f
crc32_poly = . + 3 /* gross, but saves a few bytes */
xorl $0xedb88320, %ebx /* EFI CRC32 Polynomial */
3:
loop 2b /* loop over bits */
cmp %di, %si /* do we reached end of message? */
jne 1b
notl %ebx /* result correction */
pop %ax
pop %dx
ret
#endif
do_lba_read:
movw $lba_dap, %si
movb $0x42, %ah
int $0x13 /* read */
ret
/*
* Data definition block
*/
errtxt: .ascii "Error " /* runs into crlf if errcod set */
errcod: .byte 0
crlf: .asciz "\r\n"
#ifndef NO_BANNER
banner: .asciz "NetBSD GPT\r\n"
#endif
#if defined(COM_PORT) && defined(COM_BAUD)
#define COM_DIVISOR (((COM_FREQ / COM_BAUD) + 8) / 16)
com_args:
.byte 0x80 /* divisor latch enable */
.byte +3 /* io_port + 3 */
.byte COM_DIVISOR & 0xff
.byte -3 /* io_port */
.byte COM_DIVISOR >> 8 /* high baud */
.byte +1 /* io_port + 1 */
.byte 0x03 /* 8 bit no parity */
.byte +2 /* io_port + 3 */
num_com_args = (. - com_args)/2
#endif
/*
* Control block for int-13 LBA read - Disk Address Packet
*/
lba_dap:
.byte 0x10 /* control block length */
.byte 0 /* reserved */
lba_count:
.word GPT_LBASZ /* sector count */
lba_rbuff:
.word GPTBUFADDR /* offset in segment */
.word 0 /* segment */
lba_sector:
.quad GPTHDR_BLKNO /* sector # goes here... */
efihdr_sign:
.ascii "EFI PART" /* GPT header signature */
.long 0x00010000 /* GPT header revision */
sizeof_efihdr_sign = . - efihdr_sign
/*
* Stuff from here on is overwritten by gpt/fdisk - the offset must not change
*
* Get amount of space to makefile can report it.
* (Unfortunately I can't seem to get the value reported when it is -ve)
*/
mbr_space = bootptn_guid - .
/*
* GUID of the bootable partition. Patchable area.
* Default GUID used by installer for safety checks.
*/
. = start + MBR_GPT_GUID_OFFSET
bootptn_guid:
/* MBR_GPT_GUID_DEFAULT */
.long 0xeee69d04
.word 0x02f4
.word 0x11e0
.byte 0x8f,0x5d
.byte 0x00,0xe0,0x81,0x52,0x9a,0x6b
/* space for mbr_dsn */
. = start + MBR_DSN_OFFSET
.long 0
/* mbr_bootsel_magic */
. = start + MBR_BS_MAGIC_OFFSET
.word 0
/* mbr partition table */
. = start + MBR_PART_OFFSET
parttab:
.fill 0x40, 0x01, 0x00
. = start + MBR_MAGIC_OFFSET
.word MBR_MAGIC
/* zeroed data space */
bss_off = 0
bss_start = .
#define BSS(name, size) name = bss_start + bss_off; bss_off = bss_off + size
BSS(dump_eax_buff, 16)
BSS(bss_end, 0)