Home | History | Annotate | Line # | Download | only in bootxx
      1 /*	$NetBSD: pbr.S,v 1.19 2011/01/06 01:08:48 jakllsch Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2003,2004 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by David Laight.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * i386 partition boot code
     34  *
     35  * This code resides in sector zero of the netbsd partition, or sector
     36  * zero of an unpartitioned disk (eg a floppy).
     37  * Sector 1 is assumed to contain the netbsd disklabel.
     38  * Sectors 2 until the end of the track contain the next phase of bootstrap.
     39  * Which know how to read the interactive 'boot' program from filestore.
     40  * The job of this code is to read in the phase 1 bootstrap.
     41  *
     42  * Makefile supplies:
     43  * PRIMARY_LOAD_ADDRESS:	Address we load code to (0x1000).
     44  * BOOTXX_SECTORS:		Number of sectors we load (15).
     45  * X86_BOOT_MAGIC_1:		A random magic number.
     46  *
     47  * Although this code is executing at 0x7c00, it is linked to address 0x1000.
     48  * All data references MUST be fixed up using R().
     49  */
     50 
     51 #include <machine/asm.h>
     52 #include <sys/bootblock.h>
     53 
     54 #define	OURADDR		0x7c00		/* our address */
     55 #define BOOTADDR	PRIMARY_LOAD_ADDRESS
     56 
     57 #define R(a) (a - BOOTADDR + OURADDR)
     58 
     59 #define lba_info R(_lba_info)
     60 #define lba_sector R(_lba_sector)
     61 #define errtxt R(_errtxt)
     62 #define errcod R(_errcod)
     63 #define newline R(_newline)
     64 
     65 #define TABENTRYSIZE	(MBR_BS_PARTNAMESIZE + 1)
     66 #define NAMETABSIZE	(4 * TABENTRYSIZE)
     67 
     68 #ifdef BOOT_FROM_FAT
     69 #define MBR_AFTERBPB	90		/* BPB size in FAT32 partition BR */
     70 #else
     71 #define MBR_AFTERBPB	62		/* BPB size in floppy master BR */
     72 #endif
     73 
     74 #ifdef TERSE_ERROR
     75 /*
     76  * Error codes. Done this way to save space.
     77  */
     78 #define ERR_READ	'2'		/* Read error */
     79 #define ERR_NO_BOOTXX	'B'		/* No bootxx_xfs in 3rd sector */
     80 #define	ERR_PTN		'P'		/* partition not defined */
     81 #define	ERR_NO_LBA	'L'		/* sector above chs limit */
     82 
     83 #define	set_err(err)	movb	$err, %al
     84 
     85 #else
     86 #define	set_err(err)	mov	$R(err), %ax
     87 #endif
     88 
     89 /*
     90  * This code is loaded to addresss 0:7c00 by either the system BIOS
     91  * (for a floppy) or the mbr boot code.  Since the boot program will
     92  * be loaded to address 1000:0, we don't need to relocate ourselves
     93  * and can load the subsequent blocks (that load boot) to an address
     94  * of our choosing. 0:1000 is a not unreasonable choice.
     95  *
     96  * On entry the BIOS drive number is in %dl and %esi may contain the
     97  * sector we were loaded from (if we were loaded by NetBSD mbr code).
     98  * In any case we have to re-read sector zero of the disk and hunt
     99  * through the BIOS partition table for the NetBSD partition.
    100  *
    101  * Or, we may have been loaded by a GPT hybrid MBR, handoff state is
    102  * specified in T13 EDD-4 annex A.
    103  */
    104 
    105 	.text
    106 	.code16
    107 ENTRY(start)
    108 	/*
    109 	 * The PC BIOS architecture defines a Boot Parameter Block (BPB) here.
    110 	 * The actual format varies between different MS-DOS versions, but
    111 	 * apparently some system BIOS insist on patching this area
    112 	 * (especially on LS120 drives - which I thought had an MBR...).
    113 	 * The initial jmp and nop are part of the standard and may be
    114 	 * tested for by the system BIOS.
    115 	 */
    116 	jmp	start0
    117 	nop
    118 	.ascii	"NetBSD60"		/* oemname (8 bytes) */
    119 
    120 	. = start + MBR_BPB_OFFSET	/* move to start of BPB */
    121 					/* (ensures oemname doesn't overflow) */
    122 
    123 	. = start + MBR_AFTERBPB	/* skip BPB */
    124 start0:
    125 	xor	%cx, %cx		/* don't trust values of ds, es or ss */
    126 	mov	%cx, %ss
    127 	mov	%cx, %sp
    128 	mov	%cx, %es
    129 #ifndef BOOT_FROM_FAT
    130 	cmpl	$0x54504721, %eax	/* did a GPT hybrid MBR start us? */
    131 	je	boot_gpt
    132 #endif
    133 	mov	%cx, %ds
    134 	xor	%ax, %ax
    135 
    136 	/* A 'reset disk system' request is traditional here... */
    137 	push	%dx			/* some BIOS zap %dl here :-( */
    138 	int	$0x13			/* ah == 0 from code above */
    139 	pop	%dx
    140 
    141 	/* Read from start of disk */
    142 	incw	%cx			/* track zero sector 1 */
    143 	movb	%ch, %dh		/* dh = head = 0 */
    144 	call	chs_read
    145 
    146 /* See if this is our code, if so we have already loaded the next stage */
    147 
    148 	xorl	%ebp, %ebp		/* pass sector 0 to next stage */
    149 	movl	(%bx), %eax		/* MBR code shouldn't even have ... */
    150 	cmpl	R(start), %eax		/* ... a jmp at the start. */
    151 	je	pbr_read_ok1
    152 
    153 /* Now scan the MBR partition table for a netbsd partition */
    154 
    155 	xorl	%ebx, %ebx		/* for base extended ptn chain */
    156 scan_ptn_tbl:
    157 	xorl	%ecx, %ecx		/* for next extended ptn */
    158 	movw	$BOOTADDR + MBR_PART_OFFSET, %di
    159 1:	movb	4(%di), %al		/* mbrp_type */
    160 	movl	8(%di), %ebp		/* mbrp_start == LBA sector */
    161 	addl	lba_sector, %ebp	/* add base of extended partition */
    162 #ifdef BOOT_FROM_FAT
    163 	cmpb	$MBR_PTYPE_FAT12, %al
    164 	je	5f
    165 	cmpb	$MBR_PTYPE_FAT16S, %al
    166 	je	5f
    167 	cmpb	$MBR_PTYPE_FAT16B, %al
    168 	je	5f
    169 	cmpb	$MBR_PTYPE_FAT32, %al
    170 	je	5f
    171 	cmpb	$MBR_PTYPE_FAT32L, %al
    172 	je	5f
    173 	cmpb	$MBR_PTYPE_FAT16L, %al
    174 	je	5f
    175 #else
    176 	cmpb	$MBR_PTYPE_NETBSD, %al
    177 #endif
    178 	jne	10f
    179 5:	testl	%esi, %esi		/* looking for a specific sector? */
    180 	je	boot
    181 	cmpl	%ebp, %esi		/* ptn we wanted? */
    182 	je	boot
    183 	/* check for extended partition */
    184 10:	cmpb	$MBR_PTYPE_EXT, %al
    185 	je	15f
    186 	cmpb	$MBR_PTYPE_EXT_LBA, %al
    187 	je	15f
    188 	cmpb	$MBR_PTYPE_EXT_LNX, %al
    189 	jne	20f
    190 15:	movl	8(%di), %ecx		/* sector of next ext. ptn */
    191 20:	add	$0x10, %di
    192 	cmp	$BOOTADDR + MBR_MAGIC_OFFSET, %di
    193 	jne	1b
    194 
    195 	/* not in base partitions, check extended ones */
    196 	jecxz	no_netbsd_ptn
    197 	testl	%ebx, %ebx
    198 	jne	30f
    199 	xchgl	%ebx, %ecx		/* save base of ext ptn chain */
    200 30:	addl	%ebx, %ecx		/* address this ptn */
    201 	movl	%ecx, lba_sector	/* sector to read */
    202 	call	read_lba
    203 	jmp	scan_ptn_tbl
    204 
    205 no_netbsd_ptn:
    206 	/* Specific sector not found: try again looking for first NetBSD ptn */
    207 	testl	%esi, %esi
    208 	set_err(ERR_PTN)
    209 	jz	error
    210 	xorl	%esi, %esi
    211 	movl	%esi, lba_sector
    212 	jmp	start
    213 
    214 /*
    215  * Sector below CHS limit
    216  * Do a cylinder-head-sector read instead
    217  * I believe the BIOS should do reads that cross track boundaries.
    218  * (but the read should start at the beginning of a track...)
    219  */
    220 read_chs:
    221 	movb	1(%di), %dh			/* head */
    222 	movw	2(%di), %cx			/* ch=cyl, cl=sect */
    223 	call	chs_read
    224 pbr_read_ok1:
    225 	jmp	pbr_read_ok
    226 
    227 /*
    228  * Active partition pointed to by di.
    229  *
    230  * We can either do a CHS (Cylinder Head Sector) or an LBA (Logical
    231  * Block Address) read.  Always doing the LBA one
    232  * would be nice - unfortunately not all systems support it.
    233  * Also some may contain a separate (eg SCSI) BIOS that doesn't
    234  * support it even when the main BIOS does.
    235  *
    236  * The safest thing seems to be to find out whether the sector we
    237  * want is inside the CHS sector count.  If it is we use CHS, if
    238  * outside we use LBA.
    239  *
    240  * Actually we check that the CHS values reference the LBA sector,
    241  * if not we assume that the LBA sector is above the limit, or that
    242  * the geometry used (by fdisk) isn't correct.
    243  */
    244 boot:
    245 	movl	%ebp, lba_sector	/* to control block */
    246 	testl	%ebx, %ebx		/* was it an extended ptn? */
    247 	jnz	boot_lba		/* yes - boot with LBA reads */
    248 
    249 /* get CHS values from BIOS */
    250 	push	%dx				/* save drive number */
    251 	movb	$8, %ah
    252 	int	$0x13				/* chs info */
    253 
    254 /*
    255  * Validate geometry, if the CHS sector number doesn't match the LBA one
    256  * we'll do an LBA read.
    257  * calc: (cylinder * number_of_heads + head) * number_of_sectors + sector
    258  * and compare against LBA sector number.
    259  * Take a slight 'flier' and assume we can just check 16bits (very likely
    260  * to be true because the number of sectors per track is 63).
    261  */
    262 	movw	2(%di), %ax			/* cylinder + sector */
    263 	push	%ax				/* save for sector */
    264 	shr	$6, %al
    265 	xchgb	%al, %ah			/* 10 bit cylinder number */
    266 	shr	$8, %dx				/* last head */
    267 	inc	%dx				/* number of heads */
    268 	mul	%dx
    269 	mov	1(%di), %dl			/* head we want */
    270 	add	%dx, %ax
    271 	and	$0x3f, %cx			/* number of sectors */
    272 	mul	%cx
    273 	pop	%dx				/* recover sector we want */
    274 	and	$0x3f, %dx
    275 	add	%dx, %ax
    276 	dec	%ax
    277 	pop	%dx				/* recover drive nmber */
    278 
    279 	cmp	%bp, %ax
    280 	je	read_chs
    281 
    282 check_lba:
    283 #ifdef NO_LBA_CHECK
    284 	jmp	boot_lba
    285 #else
    286 /*
    287  * Determine whether we have int13-extensions, by calling
    288  * int 13, function 41. Check for the magic number returned,
    289  * and the disk packet capability.
    290  *
    291  * This is actually relatively pointless:
    292  * 1) we only use LBA reads if CHS ones would fail
    293  * 2) the MBR code managed to read the same sectors
    294  * 3) the BIOS will (ok should) reject the LBA read as a bad BIOS call
    295  */
    296 	movw	$0x55aa, %bx
    297 	movb	$0x41, %ah
    298 	int	$0x13
    299 	jc	1f				/* no int13 extensions */
    300 	cmpw	$0xaa55, %bx
    301 	jnz	1f
    302 	testb	$1, %cl
    303 	jnz	boot_lba
    304 1:	set_err(ERR_NO_LBA)
    305 #endif	/* NO_LBA_CHECK */
    306 
    307 /*
    308  * Something went wrong,
    309  * Output error code,
    310  */
    311 
    312 error:
    313 #ifdef TERSE_ERROR
    314 	movb	%al, errcod
    315 	movw	$errtxt, %si
    316 	call	message
    317 #else
    318 	push	%ax
    319 	movw	$errtxt, %si
    320 	call	message
    321 	pop	%si
    322 	call	message
    323 	movw	$newline, %si
    324 	call	message
    325 #endif
    326 1:	sti
    327 	hlt
    328 	jmp	1b
    329 
    330 boot_lba:
    331 	call	read_lba
    332 
    333 /*
    334  * Check magic number for valid stage 2 bootcode
    335  * then jump into it.
    336  */
    337 pbr_read_ok:
    338 	cmpl	$X86_BOOT_MAGIC_1, bootxx_magic
    339 	set_err(ERR_NO_BOOTXX)
    340 	jnz	error
    341 
    342 	movl	%ebp, %esi			/* %esi ptn base, %dl disk id */
    343 	movl	lba_sector + 4, %edi		/* %edi ptn base high */
    344 	jmp	$0, $bootxx			/* our %cs may not be zero */
    345 
    346 /* Read disk using int13-extension parameter block */
    347 read_lba:
    348 	pusha
    349 	movw	$lba_info, %si			/* ds:si is ctl block */
    350 	movb	$0x42, %ah
    351 do_read:
    352 	int	$0x13
    353 	popa
    354 
    355 	set_err(ERR_READ)
    356 	jc	error
    357 	ret
    358 
    359 /* Read using CHS */
    360 
    361 chs_read:
    362 	movw	$BOOTADDR, %bx			/* es:bx is buffer */
    363 	pusha
    364 	movw	$0x200 + BOOTXX_SECTORS, %ax	/* command 2, xx sectors */
    365 	jmp	do_read
    366 
    367 #ifndef BOOT_FROM_FAT
    368 boot_gpt:
    369 	movl	(20+32+0)(%si), %ebp
    370 	movl	(20+32+4)(%si), %edi
    371 	movw	%cx, %ds
    372 	movl	%ebp, lba_sector + 0
    373 	movl	%edi, lba_sector + 4
    374 	movl	%ebp, %esi
    375 	jmp	boot_lba
    376 #endif
    377 
    378 _errtxt: .ascii	"Error "			/* runs into newline... */
    379 _errcod: .byte	0				/* ... if errcod set */
    380 _newline:
    381 	.asciz	"\r\n"
    382 
    383 #ifndef TERSE_ERROR
    384 ERR_READ:	.asciz	"read"
    385 ERR_NO_BOOTXX:	.asciz	"no magic"
    386 ERR_PTN:	.asciz	"no slice"
    387 #ifndef NO_LBA_CHECK
    388 ERR_NO_LBA:	.asciz	"need LBA"
    389 #endif
    390 #endif
    391 
    392 /*
    393  * I hate #including source files, but pbr_magic below has to be at
    394  * the correct absolute address.
    395  * Clearly this could be done with a linker script.
    396  */
    397 
    398 #include <message.S>
    399 #if 0
    400 #include <dump_eax.S>
    401 #endif
    402 
    403 /* Control block for int-13 LBA read. */
    404 _lba_info:
    405 	.word	0x10				/* control block length */
    406 	.word	BOOTXX_SECTORS			/* sector count */
    407 	.word	BOOTADDR			/* offset in segment */
    408 	.word	0				/* segment */
    409 _lba_sector:
    410 	.quad	0				/* sector # goes here... */
    411 
    412 /* Drive Serial Number */
    413 	. = _C_LABEL(start) + MBR_DSN_OFFSET
    414 	.long	0
    415 
    416 /* mbr_bootsel_magic (not used here) */
    417 	. = _C_LABEL(start) + MBR_BS_MAGIC_OFFSET
    418 	.word	0
    419 
    420 /*
    421  * Provide empty MBR partition table.
    422  * If this is installed as an MBR, the user can use fdisk(8) to create
    423  * the correct partition table ...
    424  */
    425 	. = _C_LABEL(start) + MBR_PART_OFFSET
    426 _pbr_part0:
    427 	.byte	0, 0, 0, 0, 0, 0, 0, 0
    428 	.long	0, 0
    429 _pbr_part1:
    430 	.byte	0, 0, 0, 0, 0, 0, 0, 0
    431 	.long	0, 0
    432 _pbr_part2:
    433 	.byte	0, 0, 0, 0, 0, 0, 0, 0
    434 	.long	0, 0
    435 _pbr_part3:
    436 	.byte	0, 0, 0, 0, 0, 0, 0, 0
    437 	.long	0, 0
    438 
    439 /*
    440  * The magic comes last
    441  */
    442 	. = _C_LABEL(start) + MBR_MAGIC_OFFSET
    443 pbr_magic:
    444 	.word	MBR_MAGIC
    445