Home | History | Annotate | Download | only in mbr
      1 /* -----------------------------------------------------------------------
      2  *
      3  *   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2009 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 /*
     30  * Modified MBR code used on an ISO image in hybrid mode.
     31  *
     32  * This doesn't follow the El Torito spec at all -- it is just a stub
     33  * loader of a hard-coded offset, but that's good enough to load
     34  * ISOLINUX.
     35  */
     36 
     37 #include "adjust.h"
     38 
     39 	.code16
     40 	.text
     41 
     42 HYBRID_MAGIC			= 0x7078c0fb
     43 isolinux_hybrid_signature	= 0x7c00+64
     44 isolinux_start_hybrid		= 0x7c00+64+4
     45 
     46 	.globl	bootsec
     47 /* Important: the top 6 words on the stack are passed to isolinux.bin */
     48 stack		= 0x7c00
     49 partoffset	= (stack-8)
     50 driveno		= (stack-14)
     51 heads		= (stack-16)
     52 sectors		= (stack-18)
     53 ebios_flag	= (stack-20)
     54 secpercyl	= (stack-24)
     55 
     56 BIOS_kbdflags	= 0x417
     57 BIOS_page	= 0x462
     58 
     59 	/* gas/ld has issues with doing this as absolute addresses... */
     60 	.section ".bootsec", "a", @nobits
     61 	.globl	bootsec
     62 bootsec:
     63 	.space	512
     64 
     65 	.text
     66 	.globl	_start
     67 _start:
     68 	.byte	0x33, 0xed	/* xorw	%bp, %bp */
     69 	nop
     70 	nop
     71 	nop
     72 	nop
     73 	nop
     74 	nop
     75 	nop
     76 	nop
     77 	nop
     78 	nop
     79 	nop
     80 	nop
     81 	nop
     82 	nop
     83 	nop
     84 	nop
     85 	nop
     86 	nop
     87 	nop
     88 	nop
     89 	nop
     90 	nop
     91 	nop
     92 	nop
     93 	nop
     94 	nop
     95 	nop
     96 	nop
     97 	nop
     98 	nop
     99 	.byte	0x33, 0xed	/* xorw	%bp, %bp */
    100 	cli
    101 	movw	%bp, %ss
    102 	movw	$stack, %sp
    103 	sti
    104 	cld
    105 
    106 	/* Check to see if we have a partition table entry */
    107 	xorl	%ebx, %ebx
    108 	xorl	%ecx, %ecx
    109 #ifdef PARTITION_SUPPORT
    110 	andw	%si, %si		/* %si == 0 -> no partition data */
    111 	jz	1f
    112 	testb	$0x7f, (%si)		/* Invalid active flag field? */
    113 	jnz	1f
    114 	cmpb	%cl, 4(%si)		/* Partition type zero == invalid? */
    115 	je	1f
    116 	cmpl	$0x58504721, %eax	/* !GPT signature in EAX? */
    117 	jne	2f
    118 	cmpb	$0xed, 4(%si)		/* EFI partition type? */
    119 	jne	2f
    120 
    121 	/* We have GPT partition information */
    122 	movl	(32+20)(%si), %ecx
    123 	movl	(36+20)(%si), %ebx
    124 	jmp	1f
    125 
    126 	/* We have non-GPT partition information */
    127 2:
    128 	movl	8(%si), %ecx
    129 #endif
    130 1:
    131 	/* We have no partition information */
    132 	pushl	%ebx			/*  -4: partoffset_hi */
    133 	pushl	%ecx			/*  -8: partoffset_lo */
    134 	pushw	%es			/* -10: es:di -> $PnP header */
    135 	pushw	%di			/* -12: es:di -> $PnP header */
    136 
    137 	movw	%bp, %ds
    138 	movw	%bp, %es
    139 
    140 	ADJUST_DRIVE
    141 	pushw	%dx			/* -14: dl -> drive number */
    142 
    143 	/* Copy down to 0:0x600 */
    144 	movw	$0x7c00, %si
    145 	movw	$_start, %di
    146 	movw	$(512/2), %cx
    147 	rep; movsw
    148 
    149 	ljmpw	$0, $next
    150 next:
    151 
    152 	/* Check to see if we have EBIOS */
    153 	pushw	%dx		/* drive number */
    154 	movb	$0x41, %ah	/* %al == 0 already */
    155 	movw	$0x55aa, %bx
    156 	xorw	%cx, %cx
    157 	xorb	%dh, %dh
    158 	stc
    159 	int	$0x13
    160 	jc	1f
    161 	cmpw	$0xaa55, %bx
    162 	jne	1f
    163 	andw	$1,%cx		/* Bit 0 = fixed disk subset */
    164 	jz	1f
    165 
    166 	/* We have EBIOS; patch in the following code at
    167 	   read_sector_cbios: movb $0x42, %ah ;  jmp read_common */
    168 	movl	$0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
    169 		(read_sector_cbios)
    170 	jmp	1f
    171 1:
    172 	popw	%dx
    173 	pushw	%cx		/* EBIOS flag */
    174 
    175 	/* Get (C)HS geometry */
    176 	movb	$0x08, %ah
    177 	int	$0x13
    178 	andw	$0x3f, %cx	/* Sector count */
    179 	popw	%bx		/* EBIOS flag */
    180 	pushw	%cx		/* -16: Save sectors on the stack */
    181 	movzbw	%dh, %ax	/* dh = max head */
    182 	incw	%ax		/* From 0-based max to count */
    183 	pushw	%ax		/* -18: Save heads on the stack */
    184 	mulw	%cx		/* Heads*sectors -> sectors per cylinder */
    185 
    186 	pushw	%bx		/* -20: EBIOS flag */
    187 
    188 	/* Save sectors/cylinder on the stack */
    189 	pushw	%dx		/* -22: High word */
    190 	pushw	%ax		/* -24: Low word */
    191 
    192 	/*
    193 	 * Load sectors.  We do this one at a time mostly to avoid
    194 	 * pitfalls and to share code with the stock MBR code.
    195 	 */
    196 	movw	$0x7c00, %bx
    197 	movw	$4, %cx		/* Sector count */
    198 	movl	(lba_offset), %eax
    199 
    200 2:
    201 	call	read_sector
    202 	jc	disk_error
    203 	incl	%eax
    204 	addb	$(512 >> 8), %bh
    205 	loopw	2b
    206 
    207 	/*
    208 	 * Okay, that actually worked... update the stack pointer
    209 	 * and jump into isolinux.bin...
    210 	 */
    211 	cmpl	$HYBRID_MAGIC,(isolinux_hybrid_signature)
    212 	jne	bad_signature
    213 
    214 	cli
    215 	movw	$ebios_flag, %sp
    216 
    217 	/*
    218 	 * Use a ljmpw here to work around a bug in some unknown version
    219 	 * of gas or ld when it comes to jumping to an absolute symbol...
    220 	 *
    221 	 * Look more closely into it if we ever are short on space.
    222 	 */
    223 	ljmpw	$0, $isolinux_start_hybrid
    224 
    225 bad_signature:
    226 	call	error
    227 	.ascii	"isolinux.bin missing or corrupt.\r\n"
    228 
    229 /*
    230  * read_sector: read a single sector pointed to by %eax to %es:%bx.
    231  * CF is set on error.  All registers saved.
    232  */
    233 read_sector:
    234 	pushal
    235 	xorl	%edx, %edx
    236 	addl	(partoffset), %eax
    237 	adcl	(partoffset+4), %edx
    238 	pushl	%edx	/* MSW of LBA */
    239 	pushl	%eax	/* LSW of LBA */
    240 	pushw	%es	/* Buffer segment */
    241 	pushw	%bx	/* Buffer offset */
    242 	pushw	$1	/* Sector count */
    243 	pushw	$16	/* Size of packet */
    244 	movw	%sp, %si
    245 
    246 	/* This chunk is skipped if we have ebios */
    247 	/* Do not clobber %eax before this chunk! */
    248 	/* This also relies on %bx and %edx as set up above. */
    249 read_sector_cbios:
    250 	divl	(secpercyl)
    251 	shlb	$6, %ah
    252 	movb	%ah, %cl
    253 	movb	%al, %ch
    254 	xchgw	%dx, %ax
    255 	divb	(sectors)
    256 	movb	%al, %dh
    257 	orb	%ah, %cl
    258 	incw	%cx	/* Sectors are 1-based */
    259 	movw	$0x0201, %ax
    260 
    261 read_common:
    262 	movb	(driveno), %dl
    263 	int	$0x13
    264 	leaw	16(%si), %sp	/* Drop DAPA */
    265 	popal
    266 	ret
    267 
    268 disk_error:
    269 	call	error
    270 	.ascii	"Operating system load error.\r\n"
    271 
    272 /*
    273  * Print error messages.  This is invoked with "call", with the
    274  * error message at the return address.
    275  */
    276 error:
    277 	popw	%si
    278 2:
    279 	lodsb
    280 	movb	$0x0e, %ah
    281 	movb	(BIOS_page), %bh
    282 	movb	$0x07, %bl
    283 	int	$0x10		/* May destroy %bp */
    284 	cmpb	$10, %al	/* Newline? */
    285 	jne	2b
    286 
    287 	int	$0x18		/* Boot failure */
    288 die:
    289 	hlt
    290 	jmp	die
    291 
    292 	/* Address of pointer to isolinux.bin */
    293 lba_offset = _start+432
    294