Home | History | Annotate | Download | only in stage2
      1 /*
      2  *  GRUB  --  GRand Unified Bootloader
      3  *  Copyright (C) 1999,2000,2001   Free Software Foundation, Inc.
      4  *
      5  *  This program is free software; you can redistribute it and/or modify
      6  *  it under the terms of the GNU General Public License as published by
      7  *  the Free Software Foundation; either version 2 of the License, or
      8  *  (at your option) any later version.
      9  *
     10  *  This program is distributed in the hope that it will be useful,
     11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *  GNU General Public License for more details.
     14  *
     15  *  You should have received a copy of the GNU General Public License
     16  *  along with this program; if not, write to the Free Software
     17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     18  */
     19 
     20 #define ASM_FILE
     21 #include <shared.h>
     22 
     23 #ifndef STAGE1_5
     24 #include <stage2_size.h>
     25 #endif
     26 
     27 /*
     28  *  defines for the code go here
     29  */
     30 
     31 	/* Absolute addresses
     32 	   This makes the assembler generate the address without support
     33 	   from the linker. (ELF can't relocate 16-bit addresses!) */
     34 #ifdef STAGE1_5
     35 # define ABS(x) (x-_start+0x2000)
     36 #else
     37 # define ABS(x) (x-_start+0x8000)
     38 #endif /* STAGE1_5 */
     39 
     40 	/* Print message string */
     41 #define MSG(x)	movw $ABS(x), %si; call message
     42 
     43 	.file	"start.S"
     44 
     45 	.text
     46 
     47 	/* Tell GAS to generate 16-bit instructions so that this code works
     48 	   in real mode. */
     49 	.code16
     50 
     51 	.globl	start, _start
     52 start:
     53 _start:
     54 	/*
     55 	 * _start is loaded at 0x8000 and is jumped to with
     56 	 * CS:IP 0:0x8000 in stage2.
     57 	 */
     58 
     59 	/*
     60 	 * we continue to use the stack for stage1 and assume that
     61 	 * some registers are set to correct values. See stage1.S
     62 	 * for more information.
     63 	 */
     64 
     65 	/* save drive reference first thing! */
     66 	pushw	%dx
     67 
     68 	/* print a notification message on the screen */
     69 	pushw	%si
     70 	MSG(notification_string)
     71 	popw	%si
     72 
     73 	/* this sets up for the first run through "bootloop" */
     74 	movw	$ABS(firstlist - BOOTSEC_LISTSIZE), %di
     75 
     76 	/* save the sector number of the second sector in %ebp */
     77 	movl	(%di), %ebp
     78 
     79         /* this is the loop for reading the secondary boot-loader in */
     80 bootloop:
     81 
     82 	/* check the number of sectors to read */
     83 	cmpw	$0, 4(%di)
     84 
     85 	/* if zero, go to the start function */
     86 	je	bootit
     87 
     88 setup_sectors:
     89 	/* check if we use LBA or CHS */
     90 	cmpb	$0, -1(%si)
     91 
     92 	/* jump to chs_mode if zero */
     93 	je	chs_mode
     94 
     95 lba_mode:
     96 	/* load logical sector start */
     97 	movl	(%di), %ebx
     98 
     99 	/* the maximum is limited to 0x7f because of Phoenix EDD */
    100 	xorl	%eax, %eax
    101 	movb	$0x7f, %al
    102 
    103 	/* how many do we really want to read? */
    104 	cmpw	%ax, 4(%di)	/* compare against total number of sectors */
    105 
    106 	/* which is greater? */
    107 	jg	1f
    108 
    109 	/* if less than, set to total */
    110 	movw	4(%di), %ax
    111 
    112 1:
    113 	/* subtract from total */
    114 	subw	%ax, 4(%di)
    115 
    116 	/* add into logical sector start */
    117 	addl	%eax, (%di)
    118 
    119 	/* set up disk address packet */
    120 
    121 	/* the size and the reserved byte */
    122 	movw	$0x0010, (%si)
    123 
    124 	/* the number of sectors */
    125 	movw	%ax, 2(%si)
    126 
    127 	/* the absolute address (low 32 bits) */
    128 	movl	%ebx, 8(%si)
    129 
    130 	/* the segment of buffer address */
    131 	movw	$BUFFERSEG, 6(%si)
    132 
    133 	/* save %ax from destruction! */
    134 	pushw	%ax
    135 
    136 	/* zero %eax */
    137 	xorl	%eax, %eax
    138 
    139 	/* the offset of buffer address */
    140 	movw	%ax, 4(%si)
    141 
    142 	/* the absolute address (high 32 bits) */
    143 	movl	%eax, 12(%si)
    144 
    145 
    146 /*
    147  * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
    148  *	Call with	%ah = 0x42
    149  *			%dl = drive number
    150  *			%ds:%si = segment:offset of disk address packet
    151  *	Return:
    152  *			%al = 0x0 on success; err code on failure
    153  */
    154 
    155 	movb	$0x42, %ah
    156 	int	$0x13
    157 
    158 	jc	read_error
    159 
    160 	movw	$BUFFERSEG, %bx
    161 	jmp	copy_buffer
    162 
    163 chs_mode:
    164 	/* load logical sector start (bottom half) */
    165 	movl	(%di), %eax
    166 
    167 	/* zero %edx */
    168 	xorl	%edx, %edx
    169 
    170 	/* divide by number of sectors */
    171 	divl	(%si)
    172 
    173 	/* save sector start */
    174 	movb	%dl, 10(%si)
    175 
    176 	xorl	%edx, %edx	/* zero %edx */
    177 	divl	4(%si)		/* divide by number of heads */
    178 
    179 	/* save head start */
    180 	movb	%dl, 11(%si)
    181 
    182 	/* save cylinder start */
    183 	movw	%ax, 12(%si)
    184 
    185 	/* do we need too many cylinders? */
    186 	cmpw	8(%si), %ax
    187 	jge	geometry_error
    188 
    189 	/* determine the maximum sector length of this read */
    190 	movw	(%si), %ax	/* get number of sectors per track/head */
    191 
    192 	/* subtract sector start */
    193 	subb	10(%si), %al
    194 
    195 	/* how many do we really want to read? */
    196 	cmpw	%ax, 4(%di)	/* compare against total number of sectors */
    197 
    198 
    199 	/* which is greater? */
    200 	jg	2f
    201 
    202 	/* if less than, set to total */
    203 	movw	4(%di), %ax
    204 
    205 2:
    206 	/* subtract from total */
    207 	subw	%ax, 4(%di)
    208 
    209 	/* add into logical sector start */
    210 	addl	%eax, (%di)
    211 
    212 /*
    213  *  This is the loop for taking care of BIOS geometry translation (ugh!)
    214  */
    215 
    216 	/* get high bits of cylinder */
    217 	movb	13(%si), %dl
    218 
    219 	shlb	$6, %dl		/* shift left by 6 bits */
    220 	movb	10(%si), %cl	/* get sector */
    221 
    222 	incb	%cl		/* normalize sector (sectors go
    223 					from 1-N, not 0-(N-1) ) */
    224 	orb	%dl, %cl	/* composite together */
    225 	movb	12(%si), %ch	/* sector+hcyl in cl, cylinder in ch */
    226 
    227 	/* restore %dx */
    228 	popw	%dx
    229 	pushw	%dx
    230 
    231 	/* head number */
    232 	movb	11(%si), %dh
    233 
    234 	pushw	%ax	/* save %ax from destruction! */
    235 
    236 /*
    237  * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
    238  *	Call with	%ah = 0x2
    239  *			%al = number of sectors
    240  *			%ch = cylinder
    241  *			%cl = sector (bits 6-7 are high bits of "cylinder")
    242  *			%dh = head
    243  *			%dl = drive (0x80 for hard disk, 0x0 for floppy disk)
    244  *			%es:%bx = segment:offset of buffer
    245  *	Return:
    246  *			%al = 0x0 on success; err code on failure
    247  */
    248 
    249 	movw	$BUFFERSEG, %bx
    250 	movw	%bx, %es	/* load %es segment with disk buffer */
    251 
    252 	xorw	%bx, %bx	/* %bx = 0, put it at 0 in the segment */
    253 	movb	$0x2, %ah	/* function 2 */
    254 	int	$0x13
    255 
    256 	jc	read_error
    257 
    258 	/* save source segment */
    259 	movw	%es, %bx
    260 
    261 copy_buffer:
    262 
    263 	/* load addresses for copy from disk buffer to destination */
    264 	movw	6(%di), %es	/* load destination segment */
    265 
    266 	/* restore %ax */
    267 	popw	%ax
    268 
    269 	/* determine the next possible destination address (presuming
    270 		512 byte sectors!) */
    271 	shlw	$5, %ax		/* shift %ax five bits to the left */
    272 	addw	%ax, 6(%di)	/* add the corrected value to the destination
    273 				   address for next time */
    274 
    275 	/* save addressing regs */
    276 	pusha
    277 	pushw	%ds
    278 
    279 	/* get the copy length */
    280 	shlw	$4, %ax
    281 	movw	%ax, %cx
    282 
    283 	xorw	%di, %di	/* zero offset of destination addresses */
    284 	xorw	%si, %si	/* zero offset of source addresses */
    285 	movw	%bx, %ds	/* restore the source segment */
    286 
    287 	cld		/* sets the copy direction to forward */
    288 
    289 	/* perform copy */
    290 	rep		/* sets a repeat */
    291 	movsb		/* this runs the actual copy */
    292 
    293 	/* restore addressing regs and print a dot with correct DS
    294 	   (MSG modifies SI, which is saved, and unused AX and BX) */
    295 	popw	%ds
    296 	MSG(notification_step)
    297 	popa
    298 
    299 	/* check if finished with this dataset */
    300 	cmpw	$0, 4(%di)
    301 	jne	setup_sectors
    302 
    303 	/* update position to load from */
    304 	subw	$BOOTSEC_LISTSIZE, %di
    305 
    306 	/* jump to bootloop */
    307 	jmp	bootloop
    308 
    309 /* END OF MAIN LOOP */
    310 
    311 bootit:
    312 	/* print a newline */
    313 	MSG(notification_done)
    314 	popw	%dx	/* this makes sure %dl is our "boot" drive */
    315 #ifdef STAGE1_5
    316 	ljmp	$0, $0x2200
    317 #else /* ! STAGE1_5 */
    318 	ljmp	$0, $0x8200
    319 #endif /* ! STAGE1_5 */
    320 
    321 
    322 /*
    323  * BIOS Geometry translation error (past the end of the disk geometry!).
    324  */
    325 geometry_error:
    326 	MSG(geometry_error_string)
    327 	jmp	general_error
    328 
    329 /*
    330  * Read error on the disk.
    331  */
    332 read_error:
    333 	MSG(read_error_string)
    334 
    335 general_error:
    336 	MSG(general_error_string)
    337 
    338 /* go here when you need to stop the machine hard after an error condition */
    339 stop:	jmp	stop
    340 
    341 #ifdef STAGE1_5
    342 notification_string:	.string "Loading stage1.5"
    343 #else
    344 notification_string:	.string "Loading stage2"
    345 #endif
    346 
    347 notification_step:	.string "."
    348 notification_done:	.string "\r\n"
    349 
    350 geometry_error_string:	.string "Geom"
    351 read_error_string:	.string "Read"
    352 general_error_string:	.string " Error"
    353 
    354 /*
    355  * message: write the string pointed to by %si
    356  *
    357  *   WARNING: trashes %si, %ax, and %bx
    358  */
    359 
    360 	/*
    361 	 * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
    362 	 *	%ah = 0xe	%al = character
    363 	 *	%bh = page	%bl = foreground color (graphics modes)
    364 	 */
    365 1:
    366 	movw	$0x0001, %bx
    367 	movb	$0xe, %ah
    368 	int	$0x10		/* display a byte */
    369 
    370 	incw	%si
    371 message:
    372 	movb	(%si), %al
    373 	cmpb	$0, %al
    374 	jne	1b	/* if not end of string, jmp to display */
    375 	ret
    376 lastlist:
    377 
    378 /*
    379  *  This area is an empty space between the main body of code below which
    380  *  grows up (fixed after compilation, but between releases it may change
    381  *  in size easily), and the lists of sectors to read, which grows down
    382  *  from a fixed top location.
    383  */
    384 
    385 	.word 0
    386 	.word 0
    387 
    388 	. = _start + 0x200 - BOOTSEC_LISTSIZE
    389 
    390         /* fill the first data listing with the default */
    391 blocklist_default_start:
    392 	.long 2		/* this is the sector start parameter, in logical
    393 			   sectors from the start of the disk, sector 0 */
    394 blocklist_default_len:
    395 			/* this is the number of sectors to read */
    396 #ifdef STAGE1_5
    397 	.word 0		/* the command "install" will fill this up */
    398 #else
    399 	.word (STAGE2_SIZE + 511) >> 9
    400 #endif
    401 blocklist_default_seg:
    402 #ifdef STAGE1_5
    403 	.word 0x220
    404 #else
    405 	.word 0x820	/* this is the segment of the starting address
    406 			   to load the data into */
    407 #endif
    408 
    409 firstlist:	/* this label has to be after the list data!!! */
    410