Home | History | Annotate | Line # | Download | only in cdboot
cdboot.S revision 1.9
      1 /*	$NetBSD: cdboot.S,v 1.9 2009/10/24 10:28:30 dsl Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2005 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Bang Jun-Young.
      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  * This is a primary boot loader that loads a secondary boot loader
     34  * directly from CD without performing floppy/hard disk emulation as
     35  * described by the El Torito specification.
     36  *
     37  * TODO:
     38  *  - Support for loading secondary boot loader > 64kB
     39  */
     40 
     41 #include <machine/asm.h>
     42 #include <sys/bootblock.h>
     43 
     44 #define BOOT_ADDR	0x7c00
     45 #define BLOCK_SIZE	2048		/* Default for ISO 9660 */
     46 #define VD_LBA		16		/* LBA of Volume Descriptor (VD) */
     47 #define PVD_ADDR	0x1000		/* Where Primary VD is loaded */
     48 #define ROOTDIR_ADDR	0x1800		/* Where Root Directory is loaded */
     49 #define LOADER_ADDR	SECONDARY_LOAD_ADDRESS
     50 
     51 #ifdef BOOT_FROM_FAT
     52 #define MBR_AFTERBPB	90		/* BPB size in FAT32 partition BR */
     53 #else
     54 #define MBR_AFTERBPB	62		/* BPB size in floppy master BR */
     55 #endif
     56 
     57 /*
     58  * See src/sys/sys/bootblock.h for details.
     59  */
     60 #define MBR_PART_COUNT	4
     61 #define MBR_PART_OFFSET	446
     62 #define MBR_PART_SIZE	16		/* sizeof(struct mbr_partition) */
     63 
     64 /*
     65  * Disk error codes
     66  */
     67 #define ERROR_TIMEOUT	0x80
     68 
     69 /*
     70  * Volume Descriptor types.
     71  */
     72 #define VD_PRIMARY		1
     73 #define VD_SUPPLEMENTARY	2
     74 #define VD_TERMINATOR		255
     75 
     76 /* Only actually used entries are listed below */
     77 
     78 /*
     79  * Format of Primary Volume Descriptor (8.4)
     80  */
     81 #define PVD_ROOT_DR	156	/* Offset of Root Directory Record */
     82 
     83 /*
     84  * Format of Directory Record (9.1)
     85  */
     86 #define DR_LEN		0
     87 #define DR_EXTENT	2
     88 #define DR_DATA_LEN	10
     89 #define DR_NAME_LEN	32
     90 #define DR_NAME		33
     91 
     92 	.text
     93 	.code16
     94 ENTRY(start)
     95 	jmp	start1
     96 
     97 	. = start + MBR_AFTERBPB	/* skip BPB */
     98 	. = start + MBR_DSN_OFFSET
     99 	.long	0
    100 
    101 /* mbr_bootsel_magic (not used here) */
    102 	. = start + MBR_BS_MAGIC_OFFSET
    103 	.word	0
    104 
    105 	. = start + MBR_PART_OFFSET
    106 	. = start + MBR_MAGIC_OFFSET
    107 pbr_magic:
    108 	.word	MBR_MAGIC
    109 	.fill	512			/* reserve space for disklabel */
    110 start1:
    111 	jmp	1f
    112 	.balign	4
    113 	.long	X86_BOOT_MAGIC_1	/* checked by installboot & pbr code */
    114 boot_params:				/* space for patchable variables */
    115 	.long	1f - boot_params	/* length of this data area */
    116 #include <boot_params.S>
    117 	. = start1 + 0x80		/* Space for patching unknown params */
    118 
    119 1:	xorw	%ax, %ax
    120 	movw	%ax, %ds
    121 	movw	%ax, %es
    122 	movw	%ax, %ss
    123 	movw	$BOOT_ADDR, %sp
    124 	movw	%sp, %si
    125 	movw	$start, %di
    126 	movw	$BLOCK_SIZE/2, %cx
    127 	rep
    128 	movsw
    129 	ljmp	$0, $real_start
    130 
    131 real_start:
    132 	movb	%dl, boot_drive		/* Save boot drive number */
    133 
    134 #ifndef DISABLE_KEYPRESS
    135 	/*
    136 	 * We can skip boot wait when:
    137 	 *  - there's no hard disk present.
    138 	 *  - there's no active partition in the MBR of the 1st hard disk.
    139 	 */
    140 
    141 	/*
    142 	 * Check presence of hard disks.
    143 	 */
    144 	movw	$0x475, %si
    145 	movb	(%si), %al
    146 	testb	%al, %al
    147 	jz	boot_cdrom
    148 
    149 	/*
    150 	 * Find the active partition from the MBR.
    151 	 */
    152 	movw	$0x0201, %ax		/* %al = number of sectors to read */
    153 	movw	$BOOT_ADDR, %bx		/* %es:%bx = data buffer */
    154 	movw	$0x0001, %cx		/* %ch = low 8 bits of cylinder no */
    155 					/* %cl = high 2 bits of cyl no & */
    156 					/*       sector number */
    157 	movw	$0x0080, %dx		/* %dh = head number */
    158 					/* %dl = disk number */
    159 	int	$0x13			/* Read MBR into memory */
    160 	jc	boot_cdrom		/* CF set on error */
    161 
    162 	movb	$1, mbr_loaded
    163 	movb	$MBR_PART_COUNT, %cl
    164 	movw	$BOOT_ADDR+MBR_PART_OFFSET, %si
    165 1:
    166 	movb	(%si), %al
    167 	testb	$0x80, %al
    168 	jnz	found_active
    169 	addw	$MBR_PART_SIZE, %si
    170 	decb	%cl
    171 	testb	%cl, %cl
    172 	jnz	1b			/* If 0, no active partition found */
    173 	jmp	boot_cdrom
    174 
    175 found_active:
    176 	movw	$str_press_key, %si
    177 	call	message
    178 next_second:
    179 	movw	$str_dot, %si
    180 	call	message
    181 	decb	wait_count
    182 	jz	boot_hard_disk
    183 	xorb	%ah, %ah		/* Get system time */
    184 	int	$0x1a
    185 	movw	%dx, %di		/* %cx:%dx = number of clock ticks */
    186 	addw	$19, %di		/* 19 ~= 18.2 Hz */
    187 wait_key:
    188 	movb	$1, %ah			/* Check for keystroke */
    189 	int	$0x16
    190 	jz	not_avail		/* ZF clear if keystroke available */
    191 	xorb	%ah, %ah		/* Read key to flush keyboard buf */
    192 	int	$0x16
    193 	jmp	boot_cdrom
    194 not_avail:
    195 	xorb	%ah, %ah		/* Get system time */
    196 	int	$0x1a
    197 	cmpw	%dx, %di		/* Compare with saved time */
    198 	jnz	wait_key
    199 	jmp	next_second
    200 
    201 boot_hard_disk:
    202 	movw	$str_crlf, %si
    203 	call	message
    204 	cmpb	$1, mbr_loaded
    205 	jz	1f
    206 	movw	$0x0201, %ax		/* %al = number of sectors to read */
    207 	movw	$BOOT_ADDR, %bx		/* %es:%bx = data buffer */
    208 	movw	$0x0001, %cx		/* %ch = low 8 bits of cylinder no */
    209 					/* %cl = high 2 bits of cyl no & */
    210 					/*       sector number */
    211 	movw	$0x0080, %dx		/* %dh = head number */
    212 					/* %dl = disk number */
    213 	int	$0x13			/* Read MBR into memory */
    214 	jc	panic			/* CF set on error */
    215 1:
    216 	movw	%cs, %ax		/* Restore initial state */
    217 	movw	%ax, %ds
    218 	movw	%ax, %es
    219 	movw	$0x0080, %dx		/* %dl = boot drive number */
    220 	jmp	$0, $BOOT_ADDR		/* Jump to MBR! */
    221 	jmp	panic			/* This should be never executed */
    222 #endif /* !DISABLE_KEYPRESS */
    223 
    224 boot_cdrom:
    225 	movw	$str_banner, %si
    226 	call	message
    227 
    228 /* Read volume descriptor sectors until Primary decriptor found */
    229 	movl	$VD_LBA, %eax
    230 next_block:
    231 	movb	$1, %dh			/* Number of sectors to read */
    232 	movl	$PVD_ADDR, %ebx
    233 	call	read_sectors
    234 	cmpb	$VD_PRIMARY, (%bx)	/* Is it Primary Volume Descriptor? */
    235 	jz	pvd_found
    236 	incl	%eax
    237 	cmpb	$VD_TERMINATOR, (%bx)
    238 	jnz	next_block
    239 	movw	$str_no_pvd, %si
    240 	call	message
    241 	jmp	panic
    242 
    243 /* Read all of root directory */
    244 pvd_found:
    245 	movw	$PVD_ADDR+PVD_ROOT_DR, %bx
    246 	movl	DR_EXTENT(%bx), %eax	/* LBA of the root directory */
    247 	movl	DR_DATA_LEN(%bx), %edx
    248 	shrl	$11, %edx		/* Convert to number of sectors */
    249 	movb	%dl, %dh		/*  ... and load it to %dh */
    250 	movl	$ROOTDIR_ADDR, %ebx
    251 	call	read_sectors
    252 
    253 /* Scan directory entries searching for /boot */
    254 next_entry:
    255 	cmpb	$0, DR_LEN(%bx)
    256 	jz	last_entry
    257 	movw	%bx, %si
    258 	addw	$DR_NAME, %si
    259 	movb	DR_NAME_LEN(%bx), %cl
    260 	movw	$str_loader, %di
    261 1:
    262 	movb	(%si), %al
    263 	cmpb	%al, (%di)
    264 	jnz	fail
    265 	incw	%si
    266 	incw	%di
    267 	decb	%cl
    268 	jnz	1b
    269 	jmp	load_loader
    270 fail:
    271 	addw	DR_LEN(%bx), %bx
    272 	jmp	next_entry
    273 last_entry:
    274 	movw	$str_no_loader, %si
    275 	call	message
    276 	jmp	panic
    277 
    278 /* Found /boot, read contents to 0x1000:0 */
    279 load_loader:
    280 	movl	DR_EXTENT(%bx), %eax
    281 	movl	DR_DATA_LEN(%bx), %edx
    282 	addl	$(BLOCK_SIZE-1), %edx	/* Convert file length to */
    283 	shrl	$11, %edx		/*  ... number of sectors */
    284 	movb	%dl, %dh
    285 	movl	$LOADER_ADDR, %ebx
    286 	call	read_sectors
    287 
    288 /* Finally call into code of /boot */
    289 	movl	$boot_params, %esi	/* Provide boot_params */
    290 	xorl	%edx, %edx
    291 	movb	boot_drive, %dl
    292 	xorl	%ebx, %ebx		/* Zero sector number */
    293 	lcall	$LOADER_ADDR/16, $0
    294 	/* fall through on load failure */
    295 panic:
    296 	hlt
    297 	jmp	panic
    298 
    299 /*
    300  * Read disk sector(s) into memory
    301  *
    302  * %eax = LBA of starting sector
    303  * %ebx = buffer to store sectors
    304  * %dh = number of sectors to read
    305  */
    306 read_sectors:
    307 	pusha
    308 	movl	%eax, edd_lba
    309 	shrl	$4, %ebx		/* Convert buffer addr to seg:0 */
    310 	movw	%bx, edd_segment
    311 	movb	%dh, edd_nsecs
    312 	movb	boot_drive, %dl
    313 	movw	$edd_packet, %si
    314 read_again:
    315 	movb	$0x42, %ah
    316 	int	$0x13
    317 	jc	read_fail
    318 	popa
    319 	ret
    320 read_fail:
    321 	cmpb	$ERROR_TIMEOUT, %ah
    322 	jz	read_again
    323 	movw	$str_read_error, %si
    324 	call	message
    325 	jmp	panic
    326 
    327 /*
    328  * For debugging purpose
    329  */
    330 put_char:
    331 	pusha
    332 	movb	$0x0e, %ah
    333 	movw	$0x0001, %bx
    334 	int	$0x10
    335 	popa
    336 	ret
    337 
    338 #include <message.S>
    339 
    340 edd_packet:
    341 edd_len:	.word	16
    342 edd_nsecs:	.word	0		/* Number of sectors to transfer */
    343 edd_offset:	.word	0
    344 edd_segment:	.word	0
    345 edd_lba:	.quad	0
    346 
    347 wait_count:	.byte	6
    348 boot_drive:	.byte	0
    349 mbr_loaded:	.byte	0
    350 
    351 str_banner:	.ascii	"\r\nNetBSD/x86 cd9660 Primary Bootstrap"
    352 str_crlf:	.asciz	"\r\n"
    353 str_press_key:	.asciz	"\r\nPress any key to boot from CD"
    354 str_dot:	.asciz	"."
    355 str_read_error:	.asciz	"Can't read CD"
    356 str_no_pvd:	.asciz	"Can't find Primary Volume Descriptor"
    357 str_no_loader:	.asciz	"Can't find /boot"
    358 str_loader:	.asciz	"BOOT.;1"
    359 
    360 /* Used to calculate free bytes */
    361 free_space = end - .
    362 
    363 	. = start + BLOCK_SIZE
    364 end:
    365