Home | History | Annotate | Line # | Download | only in mbr
      1 /* -----------------------------------------------------------------------
      2  *
      3  *   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
      5  *
      6  *   Permission is hereby granted, free of charge, to any person
      7  *   obtaining a copy of this software and associated documentation
      8  *   files (the "Software"), to deal in the Software without
      9  *   restriction, including without limitation the rights to use,
     10  *   copy, modify, merge, publish, distribute, sublicense, and/or
     11  *   sell copies of the Software, and to permit persons to whom
     12  *   the Software is furnished to do so, subject to the following
     13  *   conditions:
     14  *
     15  *   The above copyright notice and this permission notice shall
     16  *   be included in all copies or substantial portions of the Software.
     17  *
     18  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     20  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     22  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     23  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     24  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     25  *   OTHER DEALINGS IN THE SOFTWARE.
     26  *
     27  * ----------------------------------------------------------------------- */
     28 
     29 #include <machine/asm.h>
     30 #include <sys/bootblock.h>
     31 
     32 #ifdef CTRL_80
     33 	.macro ADJUST_DRIVE
     34 	testb	$0x04, BIOS_kbdflags
     35 	jz	1f
     36 	movb	$0x80, %dl
     37 1:
     38 	.endm
     39 #elif defined(FORCE_80)
     40 	.macro ADJUST_DRIVE
     41 	movb	$0x80, %dl
     42 	.endm
     43 #else
     44 	.macro ADJUST_DRIVE
     45 	.endm
     46 #endif
     47 
     48 	.code16
     49 	.text
     50 
     51 	.globl	bootsec
     52 stack		= 0x7c00
     53 
     54 /* Partition table header here */
     55 phdr		= stack		/* Above the stack, overwritten by bootsect */
     56 /* Partition table sector here */
     57 /* To handle > 32K we need to play segment tricks... */
     58 psec		= _phdr + 512
     59 
     60 /* Where we put DS:SI */
     61 dssi_out	= start + 0x1be
     62 
     63 BIOS_kbdflags	= 0x417
     64 BIOS_page	= 0x462
     65 
     66 	/* gas/ld has issues with doing this as absolute addresses... */
     67 	.section ".bootsec", "a", @nobits
     68 	.globl	bootsec
     69 bootsec:
     70 	.space	512
     71 
     72 	.text
     73 	.globl	start
     74 start:
     75 	cli
     76 	xorw	%ax, %ax
     77 	movw	%ax, %ds
     78 	movw	%ax, %ss
     79 	movw	$stack, %sp
     80 	movw	%sp, %si
     81 	pushw	%es		/* 4(%bp) es:di -> $PnP header */
     82 	pushw	%di		/* 2(%bp) */
     83 	movw	%ax, %es
     84 	sti
     85 	cld
     86 
     87 	/* Copy down to 0:0x600 */
     88 	movw	$start, %di
     89 	movw	$(512/2), %cx
     90 	rep; movsw
     91 
     92 	ljmpw	$0, $next
     93 next:
     94 
     95 	ADJUST_DRIVE
     96 	pushw	%dx		/* 0(%bp) = %dl -> drive number */
     97 
     98 	/* Check to see if we have EBIOS */
     99 	pushw	%dx		/* drive number */
    100 	movb	$0x41, %ah	/* %al == 0 already */
    101 	movw	$0x55aa, %bx
    102 	xorw	%cx, %cx
    103 	xorb	%dh, %dh
    104 	stc
    105 	int	$0x13
    106 	jc	1f
    107 	cmpw	$0xaa55, %bx
    108 	jne	1f
    109 	shrw	%cx		/* Bit 0 = fixed disk subset */
    110 	jnc	1f
    111 
    112 	/* We have EBIOS; patch in the following code at
    113 	   read_sector_cbios: movb $0x42, %ah ;  jmp read_common */
    114 	movl	$0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
    115 		(read_sector_cbios)
    116 
    117 1:
    118 	popw	%dx
    119 
    120 	/* Get (C)HS geometry */
    121 	movb	$0x08, %ah
    122 	int	$0x13
    123 	andw	$0x3f, %cx	/* Sector count */
    124 	movw	%sp, %bp	/* %bp -> frame pointer: LEAVE UNCHANGED */
    125 	pushw	%cx		/* -2(%bp) Save sectors on the stack */
    126 	movzbw	%dh, %ax	/* dh = max head */
    127 	incw	%ax		/* From 0-based max to count */
    128 	mulw	%cx		/* Heads*sectors -> sectors per cylinder */
    129 
    130 	/* Save sectors/cylinder on the stack */
    131 	pushw	%dx		/* -4(%bp) High word */
    132 	pushw	%ax		/* -6(%bp) Low word */
    133 
    134 	/* Load partition table header */
    135 	xorl	%eax,%eax
    136 	cltd
    137 	incw	%ax		/* %edx:%eax = 1 */
    138 	movw	$phdr, %bx
    139 	pushw	%bx		/* -8(%bp) phdr == bootsect */
    140 	call	read_sector
    141 
    142 	/* Number of partition sectors */
    143 	/* We assume the partition table is 32K or less, and that
    144 	   the sector size is 512. */
    145 	/* Note: phdr == 6(%bp) */
    146 	movw	(80+6)(%bp),%cx		/* NumberOfPartitionEntries */
    147 	movw	(84+6)(%bp),%ax		/* SizeOfPartitionEntry */
    148 	pushw	%ax
    149 	pushw	%cx
    150 	mulw	%cx
    151 	shrw	$9,%ax
    152 	xchgw	%ax,%cx
    153 	incw	%cx
    154 
    155 	/* Starting LBA of partition array */
    156 	movl	(72+6)(%bp),%eax
    157 	movl	(76+6)(%bp),%edx
    158 
    159 	pushw	%bx
    160 get_ptab:
    161 	call	read_sector
    162 	call	inc64
    163 	loop	get_ptab
    164 
    165 	/* Find the boot partition */
    166 	xorw	%si,%si			/* Nothing found yet */
    167 	popw	%di			/* Partition table in memory */
    168 	popw	%cx			/* NumberOfPartitionEntries */
    169 	popw	%ax			/* SizeOfPartitionEntry */
    170 
    171 find_part:
    172 	/* If the PartitionTypeGUID is all zero, it's an empty slot */
    173 	movl	  (%di),%edx
    174 	orl	 4(%di),%edx
    175 	orl	 8(%di),%edx
    176 	orl	12(%di),%edx
    177 	jz	not_this
    178 	testb	$0x04,48(%di)
    179 	jz	not_this
    180 	andw	%si,%si
    181 	jnz	found_multiple
    182 	movw	%di,%si
    183 not_this:
    184 	addw	%ax,%di
    185 	loop	find_part
    186 
    187 	andw	%si,%si
    188 	jnz	found_part
    189 
    190 missing_os:
    191 	call	error
    192 	.ascii	"Missing OS\r\n"
    193 
    194 found_multiple:
    195 	call	error
    196 	.ascii	"Multiple active partitions\r\n"
    197 
    198 found_part:
    199 	xchgw	%ax,%cx		/* Set up %cx for rep movsb further down */
    200 
    201 	movw	$dssi_out,%di
    202 	pushw	%di
    203 
    204 	/* 80 00 00 00 ee 00 00 00
    205 	   - bootable partition, type EFI (EE), no CHS information */
    206 	xorl	%eax,%eax
    207 	movb	$0x80,%al
    208 	stosl
    209 	movb	$0xed,%al
    210 	stosl
    211 	movl	32(%si),%eax
    212 	movl	36(%si),%edx
    213 	call	saturate_stosl		/* Partition start */
    214 
    215 	movl	40(%si),%eax
    216 	movl	44(%si),%edx
    217 	subl	32(%si),%eax
    218 	sbbl	36(%si),%edx
    219 	call	inc64
    220 	call	saturate_stosl		/* Partition length */
    221 
    222 	movzwl	%cx,%eax		/* Length of GPT entry */
    223 	stosl
    224 
    225 	rep; movsb			/* GPT entry follows MBR entry */
    226 	popw	%si
    227 
    228 /*
    229  * boot: invoke the actual bootstrap. %ds:%si points to the
    230  * partition information in memory.  The top word on the stack
    231  * is phdr == 0x7c00 == the address of the boot sector.
    232  */
    233 boot:
    234 	movl	(32+20)(%si),%eax
    235 	movl	(36+20)(%si),%edx
    236 	popw	%bx
    237 	call	read_sector
    238 	cmpw	$0xaa55, -2(%bx)
    239 	jne	missing_os	/* Not a valid boot sector */
    240 	movw	%bp, %sp	/* driveno == bootsec-6 */
    241 	popw	%dx		/* dl -> drive number */
    242 	popw	%di		/* es:di -> $PnP vector */
    243 	popw	%es
    244 	movl	$0x54504721,%eax /* !GPT magic number */
    245 	cli
    246 	jmpw	*%sp		/* %sp == bootsec */
    247 
    248 /*
    249  * Store the value in %eax to %di iff %edx == 0, otherwise store -1.
    250  * Returns the value that was actually written in %eax.
    251  */
    252 saturate_stosl:
    253 	andl	%edx,%edx
    254 	jz 1f
    255 	orl	$-1,%eax
    256 1:	stosl
    257 	ret
    258 
    259 /*
    260  * Increment %edx:%eax
    261  */
    262 inc64:
    263 	addl	$1,%eax
    264 	adcl	$0,%edx
    265 	ret
    266 
    267 /*
    268  * read_sector: read a single sector pointed to by %edx:%eax to
    269  * %es:%bx.  CF is set on error.  All registers saved.
    270  */
    271 read_sector:
    272 	pushal
    273 	pushl	%edx	/* MSW of LBA */
    274 	pushl	%eax	/* LSW of LBA */
    275 	pushw	%es	/* Buffer segment */
    276 	pushw	%bx	/* Buffer offset */
    277 	pushw	$1	/* Sector count */
    278 	pushw	$16	/* Size of packet */
    279 	movw	%sp, %si
    280 
    281 	/* This chunk is skipped if we have ebios */
    282 	/* Do not clobber %es:%bx or %edx:%eax before this chunk! */
    283 read_sector_cbios:
    284 	divl	-6(%bp)	/* secpercyl */
    285 	shlb	$6, %ah
    286 	movb	%ah, %cl
    287 	movb	%al, %ch
    288 	xchgw	%dx, %ax
    289 	divb	-2(%bp)	/* sectors */
    290 	movb	%al, %dh
    291 	orb	%ah, %cl
    292 	incw	%cx	/* Sectors are 1-based */
    293 	movw	$0x0201, %ax
    294 
    295 read_common:
    296 	movb	(%bp), %dl /* driveno */
    297 	int	$0x13
    298 	leaw	16(%si), %sp	/* Drop DAPA */
    299 	popal
    300 	jc	disk_error
    301 	addb	$2, %bh		/* bx += 512: point to the next buffer */
    302 	ret
    303 
    304 disk_error:
    305 	call	error
    306 	.ascii	"Disk error on boot\r\n"
    307 
    308 /*
    309  * Print error messages.  This is invoked with "call", with the
    310  * error message at the return address.
    311  */
    312 error:
    313 	popw	%si
    314 2:
    315 	lodsb
    316 	movb	$0x0e, %ah
    317 	movb	(BIOS_page), %bh
    318 	movb	$0x07, %bl
    319 	int	$0x10		/* May destroy %bp */
    320 	cmpb	$10, %al	/* Newline? */
    321 	jne	2b
    322 
    323 	int	$0x18		/* Boot failure */
    324 die:
    325 	hlt
    326 	jmp	die
    327 
    328 mbr_space = end - .
    329 	. = MBR_DSN_OFFSET
    330 end:
    331