| /* -*-Asm-*- */ | 
 | /* | 
 |  *  GRUB  --  GRand Unified Bootloader | 
 |  *  Copyright (C) 1996   Erich Boleyn  <erich@uruk.org> | 
 |  *  Copyright (C) 1999,2000   Free Software Foundation, Inc. | 
 |  * | 
 |  *  This program is free software; you can redistribute it and/or modify | 
 |  *  it under the terms of the GNU General Public License as published by | 
 |  *  the Free Software Foundation; either version 2 of the License, or | 
 |  *  (at your option) any later version. | 
 |  * | 
 |  *  This program is distributed in the hope that it will be useful, | 
 |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  *  GNU General Public License for more details. | 
 |  * | 
 |  *  You should have received a copy of the GNU General Public License | 
 |  *  along with this program; if not, write to the Free Software | 
 |  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
 |  */ | 
 |  | 
 | #include <stage1.h> | 
 | 	 | 
 | /* | 
 |  *  defines for the code go here | 
 |  */ | 
 |  | 
 | 	/* Absolute addresses | 
 | 	   This makes the assembler generate the address without support | 
 | 	   from the linker. (ELF can't relocate 16-bit addresses!) */ | 
 | #define ABS(x) (x-_start+0x7c00) | 
 |  | 
 | 	/* Print message string */ | 
 | #define MSG(x)	movw $ABS(x), %si; call message | 
 |  | 
 | 	/* XXX:	binutils-2.9.1.0.x doesn't produce a short opcode for this. */ | 
 | #define	MOV_MEM_TO_AL(x)	.byte 0xa0;  .word x | 
 | 	 | 
 | 	.file	"stage1.S" | 
 |  | 
 | 	.text | 
 |  | 
 | 	/* Tell GAS to generate 16-bit instructions so that this code works | 
 | 	   in real mode. */ | 
 | 	.code16 | 
 |  | 
 | .globl _start; _start: | 
 | 	/* | 
 | 	 * _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00 | 
 | 	 */ | 
 |  | 
 | 	/* | 
 | 	 * Beginning of the sector is compatible with the FAT/HPFS BIOS | 
 | 	 * parameter block. | 
 | 	 */ | 
 |  | 
 | 	jmp	after_BPB | 
 | 	nop	/* do I care about this ??? */ | 
 |  | 
 | 	/* | 
 | 	 * This space is for the BIOS parameter block!!!!  Don't change | 
 | 	 * the first jump, nor start the code anywhere but right after | 
 | 	 * this area. | 
 | 	 */ | 
 |  | 
 | 	. = _start + 4 | 
 |  | 
 | 	/* scratch space */ | 
 | mode: | 
 | 	.byte	0 | 
 | disk_address_packet:	 | 
 | sectors: | 
 | 	.long	0 | 
 | heads: | 
 | 	.long	0 | 
 | cylinders: | 
 | 	.word	0 | 
 | sector_start: | 
 | 	.byte	0 | 
 | head_start: | 
 | 	.byte	0 | 
 | cylinder_start: | 
 | 	.word	0 | 
 | 	/* more space... */ | 
 |  | 
 | 	. = _start + STAGE1_BPBEND | 
 |  | 
 | 	/* | 
 | 	 * End of BIOS parameter block. | 
 | 	 */ | 
 |  | 
 | stage1_version:	 | 
 | 	.byte	COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR | 
 | boot_drive:	 | 
 | 	.byte 0xff	/* the disk to load stage2 from */ | 
 | 			/* 0xff means use the boot drive */ | 
 | force_lba: | 
 | 	.byte	0 | 
 | stage2_address: | 
 | 	.word	0x8000 | 
 | stage2_sector: | 
 | 	.long	1 | 
 | stage2_segment: | 
 | 	.word	0x800 | 
 |  | 
 | after_BPB: | 
 |  | 
 | /* general setup */ | 
 | 	cli		/* we're not safe here! */ | 
 |  | 
 | 	/* | 
 | 	 * ljmp to the next instruction because some bogus BIOSes | 
 | 	 * jump to 07C0:0000 instead of 0000:7C00. | 
 | 	 */ | 
 | 	ljmp	$0, $ABS(real_start) | 
 |  | 
 | real_start:	 | 
 |  | 
 | 	/* set up %ds and %ss as offset from 0 */ | 
 | 	xorw	%ax, %ax | 
 | 	movw	%ax, %ds | 
 | 	movw	%ax, %ss | 
 |  | 
 | 	/* set up the REAL stack */ | 
 | 	movw	$STAGE1_STACKSEG, %sp | 
 |  | 
 | 	sti		/* we're safe again */ | 
 |  | 
 | 	/* | 
 | 	 *  Check if we have a forced disk reference here | 
 | 	 */ | 
 | 	MOV_MEM_TO_AL(ABS(boot_drive))	/* movb	ABS(boot_drive), %al */ | 
 | 	cmpb	$0xff, %al | 
 | 	je	1f | 
 | 	movb	%al, %dl | 
 | 1: | 
 | 	/* save drive reference first thing! */ | 
 | 	pushw	%dx | 
 |  | 
 | 	/* print a notification message on the screen */ | 
 | 	MSG(notification_string) | 
 |  | 
 | 	/* do not probe LBA if the drive is a floppy */ | 
 | 	testb	$STAGE1_BIOS_HD_FLAG, %dl | 
 | 	jz	chs_mode | 
 | 			 | 
 | 	/* check if LBA is supported */ | 
 | 	movb	$0x41, %ah | 
 | 	movw	$0x55aa, %bx | 
 | 	int	$0x13 | 
 |  | 
 | 	/* use CHS if fails */ | 
 | 	jc	chs_mode | 
 | 	cmpw	$0xaa55, %bx | 
 | 	jne	chs_mode | 
 |  | 
 | 	/* check if AH=0x42 is supported if FORCE_LBA is zero */ | 
 | 	MOV_MEM_TO_AL(ABS(force_lba))	/* movb	ABS(force_lba), %al */ | 
 | 	testb	%al, %al | 
 | 	jnz	skip_lba_bitmap_check | 
 | 	andw	$1, %cx | 
 | 	jz	chs_mode | 
 | 	 | 
 | skip_lba_bitmap_check: | 
 | 	 | 
 | 	/* get the geometry (limited to 2TB!) */ | 
 | 	movb	$0x48, %ah | 
 | 	movw	$STAGE1_DRP_ADDR, %si | 
 | 	movw	$STAGE1_DRP_SIZE, (%si) | 
 | 	int	$0x13 | 
 |  | 
 | 	/* use CHS if fails */ | 
 | 	jc	chs_mode | 
 |  | 
 | lba_mode: | 
 | 	/* save the total number of sectors */ | 
 | 	movl	0x10(%si), %ecx | 
 |  | 
 | 	/* set %si to the disk address packet */ | 
 | 	movw	$ABS(disk_address_packet), %si | 
 |  | 
 | 	/* set the mode to non-zero */ | 
 | 	movb	$1, -1(%si) | 
 | 	 | 
 | 	movl	ABS(stage2_sector), %ebx | 
 |  | 
 | 	/* check for the geometry */ | 
 | #ifdef NO_BUGGY_BIOS_IN_THE_WORLD | 
 | 	cmpl	%ecx, %ebx | 
 | 	jge	geometry_error | 
 | #endif /* NO_BUGGY_BIOS_IN_THE_WORLD */ | 
 | 	 | 
 | 	/* the size and the reserved byte */ | 
 | 	movw	$0x0010, (%si) | 
 |  | 
 | 	/* the blocks */ | 
 | 	movw	$1, 2(%si) | 
 | 	 | 
 | 	/* the absolute address (low 32 bits) */ | 
 | 	movl	%ebx, 8(%si) | 
 |  | 
 | 	/* the segment of buffer address */ | 
 | 	movw	$STAGE1_BUFFERSEG, 6(%si) | 
 |  | 
 | 	xorl	%eax, %eax | 
 | 	movw	%ax, 4(%si) | 
 | 	movl	%eax, 12(%si) | 
 | 	 | 
 | /* | 
 |  * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory | 
 |  *	Call with	%ah = 0x42 | 
 |  *			%dl = drive number | 
 |  *			%ds:%si = segment:offset of disk address packet | 
 |  *	Return: | 
 |  *			%al = 0x0 on success; err code on failure | 
 |  */ | 
 |  | 
 | 	movb	$0x42, %ah | 
 | 	int	$0x13 | 
 |  | 
 | 	/* LBA read is not supported, so fallback to CHS.  */ | 
 | 	jc	chs_mode | 
 |  | 
 | 	movw	$STAGE1_BUFFERSEG, %bx | 
 | 	jmp	copy_buffer | 
 | 		 | 
 | chs_mode:	 | 
 | 	/* | 
 | 	 *  Determine the hard disk geometry from the BIOS! | 
 | 	 *  We do this first, so that LS-120 IDE floppies work correctly. | 
 | 	 */ | 
 | 	movb	$8, %ah | 
 | 	int	$0x13 | 
 | 	jnc	final_init | 
 |  | 
 | 	/* | 
 | 	 *  The call failed, so maybe use the floppy probe instead. | 
 | 	 */ | 
 | 	testb	$STAGE1_BIOS_HD_FLAG, %dl | 
 | 	jz	floppy_probe | 
 |  | 
 | 	/* Nope, we definitely have a hard disk, and we're screwed. */ | 
 | 	jmp	hd_probe_error | 
 |  | 
 | final_init: | 
 | 	 | 
 | 	movw	$ABS(sectors), %si | 
 |  | 
 | 	/* set the mode to zero */ | 
 | 	movb	$0, -1(%si) | 
 | 	 | 
 | 	/* save number of heads */ | 
 | 	xorl	%eax, %eax | 
 | 	movb	%dh, %al | 
 | 	incw	%ax | 
 | 	movl	%eax, 4(%si) | 
 |  | 
 | 	xorw	%dx, %dx | 
 | 	movb	%cl, %dl | 
 | 	shlw	$2, %dx | 
 | 	movb	%ch, %al | 
 | 	movb	%dh, %ah | 
 |  | 
 | 	/* save number of cylinders */ | 
 | 	incw	%ax | 
 | 	movw	%ax, 8(%si) | 
 |  | 
 | 	xorw	%ax, %ax | 
 | 	movb	%dl, %al | 
 | 	shrb	$2, %al | 
 |  | 
 | 	/* save number of sectors */ | 
 | 	movl	%eax, (%si) | 
 |  | 
 | setup_sectors: | 
 | 	/* load logical sector start (bottom half) */ | 
 | 	movl	ABS(stage2_sector), %eax | 
 |  | 
 | 	/* zero %edx */ | 
 | 	xorl	%edx, %edx | 
 |  | 
 | 	/* divide by number of sectors */ | 
 | 	divl	(%si) | 
 |  | 
 | 	/* save sector start */ | 
 | 	movb	%dl, 10(%si) | 
 |  | 
 | 	xorl	%edx, %edx	/* zero %edx */ | 
 | 	divl	4(%si)		/* divide by number of heads */ | 
 |  | 
 | 	/* save head start */ | 
 | 	movb	%dl, 11(%si) | 
 |  | 
 | 	/* save cylinder start */ | 
 | 	movw	%ax, 12(%si) | 
 |  | 
 | 	/* do we need too many cylinders? */ | 
 | 	cmpw	8(%si), %ax | 
 | 	jge	geometry_error | 
 |  | 
 | /* | 
 |  *  This is the loop for taking care of BIOS geometry translation (ugh!) | 
 |  */ | 
 |  | 
 | 	/* get high bits of cylinder */ | 
 | 	movb	13(%si), %dl | 
 |  | 
 | 	shlb	$6, %dl		/* shift left by 6 bits */ | 
 | 	movb	10(%si), %cl	/* get sector */ | 
 |  | 
 | 	incb	%cl		/* normalize sector (sectors go | 
 | 					from 1-N, not 0-(N-1) ) */ | 
 | 	orb	%dl, %cl	/* composite together */ | 
 | 	movb	12(%si), %ch	/* sector+hcyl in cl, cylinder in ch */ | 
 |  | 
 | 	/* restore %dx */ | 
 | 	popw	%dx | 
 | 	 | 
 | 	/* head number */ | 
 | 	movb	11(%si), %dh | 
 |  | 
 | /* | 
 |  * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory | 
 |  *	Call with	%ah = 0x2 | 
 |  *			%al = number of sectors | 
 |  *			%ch = cylinder | 
 |  *			%cl = sector (bits 6-7 are high bits of "cylinder") | 
 |  *			%dh = head | 
 |  *			%dl = drive (0x80 for hard disk, 0x0 for floppy disk) | 
 |  *			%es:%bx = segment:offset of buffer | 
 |  *	Return: | 
 |  *			%al = 0x0 on success; err code on failure | 
 |  */ | 
 |  | 
 | 	movw	$STAGE1_BUFFERSEG, %bx | 
 | 	movw	%bx, %es	/* load %es segment with disk buffer */ | 
 |  | 
 | 	xorw	%bx, %bx	/* %bx = 0, put it at 0 in the segment */ | 
 | 	movw	$0x0201, %ax	/* function 2 */ | 
 | 	int	$0x13 | 
 |  | 
 | 	jc	read_error | 
 |  | 
 | 	movw	%es, %bx | 
 | 	 | 
 | copy_buffer: | 
 | 	movw	ABS(stage2_segment), %es | 
 |  | 
 | 	/* | 
 | 	 * We need to save %cx and %si because the startup code in | 
 | 	 * stage2 uses them without initializing them. | 
 | 	 */ | 
 | 	pusha | 
 | 	pushw	%ds | 
 | 	 | 
 | 	movw	$0x100, %cx | 
 | 	movw	%bx, %ds | 
 | 	xorw	%si, %si | 
 | 	xorw	%di, %di | 
 | 	 | 
 | 	cld | 
 | 	 | 
 | 	rep | 
 | 	movsw | 
 |  | 
 | 	popw	%ds | 
 | 	popa | 
 |  | 
 | 	/* boot stage2 */ | 
 | 	jmp	*(stage2_address) | 
 |  | 
 | /* END OF MAIN LOOP */ | 
 |  | 
 | /* | 
 |  * BIOS Geometry translation error (past the end of the disk geometry!). | 
 |  */ | 
 | geometry_error: | 
 | 	MSG(geometry_error_string) | 
 | 	jmp	general_error | 
 |  | 
 | /* | 
 |  * Disk probe failure. | 
 |  */ | 
 | hd_probe_error: | 
 | 	MSG(hd_probe_error_string) | 
 | 	jmp	general_error | 
 |  | 
 | /* | 
 |  * Read error on the disk. | 
 |  */ | 
 | read_error: | 
 | 	MSG(read_error_string) | 
 |  | 
 | general_error: | 
 | 	MSG(general_error_string) | 
 |  | 
 | /* go here when you need to stop the machine hard after an error condition */ | 
 | stop:	jmp	stop | 
 |  | 
 | notification_string:	.string "GRUB " | 
 | geometry_error_string:	.string "Geom" | 
 | hd_probe_error_string:	.string "Hard Disk" | 
 | read_error_string:	.string "Read" | 
 | general_error_string:	.string " Error" | 
 |  | 
 | /* | 
 |  * message: write the string pointed to by %si | 
 |  * | 
 |  *   WARNING: trashes %si, %ax, and %bx | 
 |  */ | 
 |  | 
 | 	/* | 
 | 	 * Use BIOS "int 10H Function 0Eh" to write character in teletype mode | 
 | 	 *	%ah = 0xe	%al = character | 
 | 	 *	%bh = page	%bl = foreground color (graphics modes) | 
 | 	 */ | 
 | 1: | 
 | 	movw	$0x0001, %bx | 
 | 	movb	$0xe, %ah | 
 | 	int	$0x10		/* display a byte */ | 
 | message: | 
 | 	lodsb | 
 | 	cmpb	$0, %al | 
 | 	jne	1b	/* if not end of string, jmp to display */ | 
 | 	ret | 
 |  | 
 | 	/* | 
 | 	 *  Windows NT breaks the compatibility by embedding a magic | 
 | 	 *  number here. | 
 | 	 */ | 
 |  | 
 | nt_magic:	 | 
 | 	. = _start + STAGE1_WINDOWS_NT_MAGIC | 
 | 	 | 
 | 	/* | 
 | 	 *  This is where an MBR would go if on a hard disk.  The code | 
 | 	 *  here isn't even referenced unless we're on a floppy.  Kinda | 
 | 	 *  sneaky, huh? | 
 | 	 */ | 
 |  | 
 | part_start:	 | 
 | 	. = _start + STAGE1_PARTSTART | 
 |  | 
 | probe_values: | 
 | 	.byte	36, 18, 15, 9, 0 | 
 |  | 
 | floppy_probe: | 
 | /* | 
 |  *  Perform floppy probe. | 
 |  */ | 
 |  | 
 | 	movw	$ABS(probe_values-1), %si | 
 |  | 
 | probe_loop: | 
 | 	/* reset floppy controller INT 13h AH=0 */ | 
 | 	xorw	%ax, %ax | 
 | 	int	$0x13 | 
 |  | 
 | 	incw	%si | 
 | 	movb	(%si), %cl | 
 |  | 
 | 	/* if number of sectors is 0, display error and die */ | 
 | 	cmpb	$0, %cl | 
 | 	jne	1f | 
 |  | 
 | /* | 
 |  * Floppy disk probe failure. | 
 |  */ | 
 | 	MSG(fd_probe_error_string) | 
 | 	jmp	general_error | 
 |  | 
 | fd_probe_error_string:	.string "Floppy" | 
 |  | 
 | 1: | 
 | 	/* perform read */ | 
 | 	movw	$STAGE1_BUFFERSEG, %bx | 
 | 	movw	$0x201, %ax | 
 | 	movb	$0, %ch | 
 | 	movb	$0, %dh | 
 | 	int	$0x13 | 
 |  | 
 | 	/* if error, jump to "probe_loop" */ | 
 | 	jc	probe_loop | 
 |  | 
 | 	/* %cl is already the correct value! */ | 
 | 	movb	$1, %dh | 
 | 	movb	$79, %ch | 
 |  | 
 | 	jmp	final_init | 
 |  | 
 | 	. = _start + STAGE1_PARTEND | 
 |  | 
 | /* the last 2 bytes in the sector 0 contain the signature */ | 
 | 	.word	STAGE1_SIGNATURE |