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 #include "adjust.h"
     30 
     31 	.code16
     32 	.text
     33 
     34 	.globl	bootsec
     35 stack		= 0x7c00
     36 driveno		= (stack-6)
     37 sectors		= (stack-8)
     38 secpercyl	= (stack-12)
     39 
     40 BIOS_kbdflags	= 0x417
     41 BIOS_page	= 0x462
     42 
     43 	/* gas/ld has issues with doing this as absolute addresses... */
     44 	.section ".bootsec", "a", @nobits
     45 	.globl	bootsec
     46 bootsec:
     47 	.space	512
     48 
     49 	.text
     50 	.globl	_start
     51 _start:
     52 	.byte	0x33, 0xc0	/* xorw	%ax, %ax */
     53 	cli
     54 	movw	%ax, %ds
     55 	movw	%ax, %ss
     56 	movw	$stack, %sp
     57 	movw	%sp, %si
     58 	pushw	%es		/* es:di -> $PnP header */
     59 	pushw	%di
     60 	movw	%ax, %es
     61 	sti
     62 	cld
     63 
     64 	/* Copy down to 0:0x600 */
     65 	movw	$_start, %di
     66 	movw	$(512/2), %cx
     67 	rep; movsw
     68 
     69 	ljmpw	$0, $next
     70 next:
     71 
     72 	ADJUST_DRIVE
     73 	pushw	%dx		/* dl -> drive number */
     74 
     75 	/* Check to see if we have EBIOS */
     76 	pushw	%dx		/* drive number */
     77 	movb	$0x41, %ah	/* %al == 0 already */
     78 	movw	$0x55aa, %bx
     79 	xorw	%cx, %cx
     80 	xorb	%dh, %dh
     81 	stc
     82 	int	$0x13
     83 	jc	1f
     84 	cmpw	$0xaa55, %bx
     85 	jne	1f
     86 	shrw	%cx		/* Bit 0 = fixed disk subset */
     87 	jnc	1f
     88 
     89 	/* We have EBIOS; patch in the following code at
     90 	   read_sector_cbios: movb $0x42, %ah ;  jmp read_common */
     91 	movl	$0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
     92 		(read_sector_cbios)
     93 
     94 1:
     95 	popw	%dx
     96 
     97 	/* Get (C)HS geometry */
     98 	movb	$0x08, %ah
     99 	int	$0x13
    100 	andw	$0x3f, %cx	/* Sector count */
    101 	pushw	%cx		/* Save sectors on the stack */
    102 	movzbw	%dh, %ax	/* dh = max head */
    103 	incw	%ax		/* From 0-based max to count */
    104 	mulw	%cx		/* Heads*sectors -> sectors per cylinder */
    105 
    106 	/* Save sectors/cylinder on the stack */
    107 	pushw	%dx		/* High word */
    108 	pushw	%ax		/* Low word */
    109 
    110 	xorl	%eax, %eax	/* Base */
    111 	cdq			/* Root (%edx <- 0) */
    112 	call	scan_partition_table
    113 
    114 	/* If we get here, we have no OS */
    115 missing_os:
    116 	call	error
    117 	.ascii	"Missing operating system.\r\n"
    118 
    119 /*
    120  * read_sector: read a single sector pointed to by %eax to 0x7c00.
    121  * CF is set on error.  All registers saved.
    122  */
    123 read_sector:
    124 	pushal
    125 	xorl	%edx, %edx
    126 	movw	$bootsec, %bx
    127 	pushl	%edx	/* MSW of LBA */
    128 	pushl	%eax	/* LSW of LBA */
    129 	pushw	%es	/* Buffer segment */
    130 	pushw	%bx	/* Buffer offset */
    131 	pushw	$1	/* Sector count */
    132 	pushw	$16	/* Size of packet */
    133 	movw	%sp, %si
    134 
    135 	/* This chunk is skipped if we have ebios */
    136 	/* Do not clobber %eax before this chunk! */
    137 	/* This also relies on %bx and %edx as set up above. */
    138 read_sector_cbios:
    139 	divl	(secpercyl)
    140 	shlb	$6, %ah
    141 	movb	%ah, %cl
    142 	movb	%al, %ch
    143 	xchgw	%dx, %ax
    144 	divb	(sectors)
    145 	movb	%al, %dh
    146 	orb	%ah, %cl
    147 	incw	%cx	/* Sectors are 1-based */
    148 	movw	$0x0201, %ax
    149 
    150 read_common:
    151 	movb	(driveno), %dl
    152 	int	$0x13
    153 	leaw	16(%si), %sp	/* Drop DAPA */
    154 	popal
    155 	ret
    156 
    157 /*
    158  * read_partition_table:
    159  *	Read a partition table (pointed to by %eax), and copy
    160  *	the partition table into the ptab buffer.
    161  *
    162  *	Clobbers %si, %di, and %cx, other registers preserved.
    163  *	%cx = 0 on exit.
    164  *
    165  *	On error, CF is set and ptab is overwritten with junk.
    166  */
    167 ptab	= _start+446
    168 
    169 read_partition_table:
    170 	call	read_sector
    171 	movw	$bootsec+446, %si
    172 	movw	$ptab, %di
    173 	movw	$(16*4/2), %cx
    174 	rep ; movsw
    175 	ret
    176 
    177 /*
    178  * scan_partition_table:
    179  *	Scan a partition table currently loaded in the partition table
    180  *	area.  Preserve all registers.
    181  *
    182  *      On entry:
    183  *	  %eax - base (location of this partition table)
    184  *	  %edx - root (offset from MBR, or 0 for MBR)
    185  *
    186  *      These get pushed into stack slots:
    187  *        28(%bp) - %eax - base
    188  *	  20(%bp) - %edx - root
    189  */
    190 
    191 scan_partition_table:
    192 	pushal
    193 	movw	%sp, %bp
    194 
    195 	/* Search for active partitions */
    196 	movw	$ptab, %bx
    197 	movw	$4, %cx
    198 	xorw	%ax, %ax
    199 	push	%bx
    200 	push	%cx
    201 5:
    202 	testb	$0x80, (%bx)
    203 	jz	6f
    204 	incw	%ax
    205 	movw	%bx, %si
    206 6:
    207 	addw	$16, %bx
    208 	loopw	5b
    209 
    210 	decw	%ax		/* Number of active partitions found */
    211 	jz	boot
    212 	jns	too_many_active
    213 
    214 	/* No active partitions found, look for extended partitions */
    215 	popw	%cx		/* %cx <- 4    */
    216 	popw	%bx		/* %bx <- ptab */
    217 7:
    218 	movb	4(%bx), %al
    219 	cmpb	$0x0f, %al	/* 0x0f = Win9x extended */
    220 	je	8f
    221 	andb	$~0x80, %al	/* 0x85 = Linux extended */
    222 	cmpb	$0x05, %al	/* 0x05 = MS-DOS extended */
    223 	jne	9f
    224 
    225 	/* It is an extended partition.  Read the extended partition and
    226 	   try to scan it.  If the scan returns, re-load the current
    227 	   partition table and resume scan. */
    228 8:
    229 	movl	8(%bx), %eax		/* Partition table offset */
    230 	movl	20(%bp), %edx		/* "Root" */
    231 	addl	%edx, %eax		/* Compute location of new ptab */
    232 	andl	%edx, %edx		/* Is this the MBR? */
    233 	jnz	10f
    234 	movl	%eax, %edx		/* Offset -> root if this was MBR */
    235 10:
    236 	call	read_partition_table
    237 	jc	11f
    238 	call	scan_partition_table
    239 11:
    240 	/* This returned, so we need to reload the current partition table */
    241 	movl	28(%bp), %eax		/* "Base" */
    242 	call	read_partition_table
    243 
    244 	/* fall through */
    245 9:
    246 	/* Not an extended partition */
    247 	addw	$16, %bx
    248 	loopw	7b
    249 
    250 	/* Nothing found, return */
    251 	popal
    252 	ret
    253 
    254 too_many_active:
    255 	call	error
    256 	.ascii	"Multiple active partitions.\r\n"
    257 
    258 /*
    259  * boot: invoke the actual bootstrap. (%si) points to the partition
    260  *	 table entry, and 28(%bp) has the partition table base.
    261  */
    262 boot:
    263 	movl	8(%si), %eax
    264 	addl	28(%bp), %eax
    265 	movl	%eax, 8(%si)	/* Adjust in-memory partition table entry */
    266 	call	read_sector
    267 	jc	disk_error
    268 
    269 	/* Check if the read sector is a XFS superblock */
    270 	cmpl	$0x42534658, (bootsec) /* "XFSB" */
    271 	jne	no_xfs
    272 
    273 	/* We put the Syslinux boot sector at offset 0x800 (4 sectors), so we
    274 	 * need to adjust %eax (%eax + 4) to read the right sector into 0x7C00.
    275 	 */
    276 	addl	$0x800 >> 0x09, %eax /* plus 4 sectors */
    277 	call	read_sector
    278 	jc	disk_error
    279 
    280 no_xfs:
    281 	cmpw	$0xaa55, (bootsec+510)
    282 	jne	missing_os		/* Not a valid boot sector */
    283 	movw	$driveno, %sp	/* driveno == bootsec-6 */
    284 	popw	%dx		/* dl -> drive number */
    285 	popw	%di		/* es:di -> $PnP vector */
    286 	popw	%es
    287 	cli
    288 	jmpw	*%sp		/* %sp == bootsec */
    289 
    290 disk_error:
    291 	call	error
    292 	.ascii	"Operating system load error.\r\n"
    293 
    294 /*
    295  * Print error messages.  This is invoked with "call", with the
    296  * error message at the return address.
    297  */
    298 error:
    299 	popw	%si
    300 2:
    301 	lodsb
    302 	movb	$0x0e, %ah
    303 	movb	(BIOS_page), %bh
    304 	movb	$0x07, %bl
    305 	int	$0x10		/* May destroy %bp */
    306 	cmpb	$10, %al	/* Newline? */
    307 	jne	2b
    308 
    309 	int	$0x18		/* Boot failure */
    310 die:
    311 	hlt
    312 	jmp	die
    313