|  | /* | 
|  | *  GRUB  --  GRand Unified Bootloader | 
|  | *  Copyright (C) 1996   Erich Boleyn  <erich@uruk.org> | 
|  | *  Copyright (C) 1999   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 | 
|  |  | 
|  | .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 */ | 
|  | stage2_sector: | 
|  | .long	1 | 
|  | stage2_address: | 
|  | .word	0x8000 | 
|  | stage2_segment: | 
|  | .word	0x800 | 
|  |  | 
|  | after_BPB: | 
|  |  | 
|  | /* general setup */ | 
|  | cli		/* we're not safe here! */ | 
|  |  | 
|  | /* 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 | 
|  | */ | 
|  | 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 | 
|  |  | 
|  | /* 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 | 
|  |  | 
|  | cmpl	%ecx, %ebx | 
|  | jge	geometry_error | 
|  |  | 
|  | /* 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 | 
|  |  | 
|  | /* should use CHS instead? */ | 
|  | jc	read_error | 
|  |  | 
|  | 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. | 
|  | */ | 
|  | pushw	%cx | 
|  | pushw	%ds | 
|  | pushw	%si | 
|  |  | 
|  | movw	$0x200, %cx | 
|  | movw	%bx, %ds | 
|  | xorw	%si, %si | 
|  | xorw	%di, %di | 
|  |  | 
|  | cld | 
|  |  | 
|  | rep | 
|  | movsb | 
|  |  | 
|  | popw	%si | 
|  | popw	%ds | 
|  | popw	%cx | 
|  |  | 
|  | /* 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 "stage1 " | 
|  | 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 */ | 
|  |  | 
|  | incw	%si | 
|  | message: | 
|  | movb	(%si), %al | 
|  | cmpb	$0, %al | 
|  | jne	1b	/* if not end of string, jmp to display */ | 
|  | ret | 
|  |  | 
|  | /* | 
|  | *  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? | 
|  | */ | 
|  |  | 
|  | . = _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 |