/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1996 Erich Boleyn * * 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. */ /* * defines for the code go here */ #define SIGNATURE 0xaa55 #define BPBEND 0x3e #define PARTSTART 0x1be /* starting address of partition table */ #define PARTEND 0x1fe /* ending addres of the partition table */ #define MINPARAMSIZ 13 /* size of extra data parameters */ #define LISTSIZ 8 /* size of sector list */ #define REALSTACK 0x2000 /* stack for this code and BIOS calls */ #define BUFFERSEG 0x7000 /* segment address of disk buffer, the disk buffer MUST be 32K long and cannot straddle a 64K boundary */ #define BIOS_HD_FLAG 0x80 /* bit set in BIOS drive number to designate a hard disk vs. a floppy */ /* Absolute addresses This makes the assembler generate the address without support from the linker. (ELF can't relocate 16bit addresses!) */ #define ABS(x) (x-_start+0x7c00) /* Relative addresses (for jumps) These macros use the local label 0, so start with your local label at 1! */ #define REL(x) .word x-0f; 0: #define RELB(x) .byte x-0f; 0: /* Print message string movw $x, %cx; call message */ #define MSG(x) movw $x, %aw; .byte 0xe8; REL(message) .file "stage1.S" .text /* * The ".code16" directive only works in GAS, the GNU assembler! * This adds 32-bit data or addressing directives so that this * code will work 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 */ .byte 0xeb; RELB(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 */ sectors: .word 0 heads: .word 0 cylinders: .word 0 /* more space... */ . = _start + BPBEND /* * End of BIOS parameter block. */ 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 $REALSTACK, %sp sti /* we're safe again */ /* * Check if we have a forced disk reference here */ /* movb firstlist, %al */ .byte 0xa0; .word ABS(firstlist) cmpb $0xff, %al /* je 1f */ .byte 0x74; RELB(1f) movb %al, %dl 1: /* save drive reference first thing! */ pushw %dx /* * Jump to floppy probe instead of the hard disk probe ? */ movb %dl, %al andb $BIOS_HD_FLAG, %al /* jz floppy_probe */ .byte 0x0f, 0x84; REL(floppy_probe) /* * Determine the hard disk geometry from the BIOS! */ movb $8, %ah int $0x13 /* if BIOS geometry call fails, display error and die! */ /* jc hd_probe_error (16-bit)*/ .byte 0x0f, 0x82; REL(hd_probe_error) final_init: xorb %ah, %ah movb %dh, %al incw %ax /* movw %ax, heads */ /* save num heads */ .byte 0xA3; .word ABS(heads) xorw %dx, %dx movb %cl, %dl shlw $2, %dx movb %ch, %al movb %dh, %ah incw %ax /* movw %ax, cylinders */ /* save num cylinders */ .byte 0xA3; .word ABS(cylinders) xorw %ax, %ax movb %dl, %al shrb $2, %al /* save a byte on addressing by moving this forward ?? */ movw $ABS(sectors), %si /* movw %ax, (%si) */ /* save num sectors */ .byte 0x89, 0x04 /* this sets up for the first run through "bootloop" */ movw $ABS(firstlist), %di /* this is the loop for reading the secondary boot-loader in */ bootloop: /* update position to load from */ subw $LISTSIZ, %di /* cmpw $0, 4(%di) */ /* check the number of sectors to read */ .byte 0x81, 0x7d, 0x04; .word 0 /* je bootit (16-bit)*/ /* if zero, go to the start function */ .byte 0x0f, 0x84; REL(bootit) /* movl (%di), %ax */ /* load logical sector start (bottom half) */ .byte 0x8b, 0x05 /* movl 2(%di), %dx */ /* load logical sector start (top half) */ .byte 0x8b, 0x55, 0x02 /* divw (%si), %dx:%ax */ /* divide by number of sectors */ .byte 0xf7, 0x34 /* movb %dl, (%di) */ /* save sector start */ .byte 0x88, 0x15 xorw %dx, %dx /* zero %dx */ /* divw 2(%si), %dx:%ax */ /* divide by number of heads */ .byte 0xf7, 0x74, 0x02 /* movb %dl, 1(%di) */ /* save head start */ .byte 0x88, 0x55, 0x01 /* movw %ax, 2(%di) */ /* save cylinder start */ .byte 0x89, 0x45, 0x02 /* cmpw 4(%si), %ax */ /* do we need too many cylinders? */ .byte 0x3b, 0x44, 0x04 /* jge geometry_error (16-bit) */ .byte 0x0f, 0x8d; REL(geometry_error) setup_sectors: /* determine the maximum sector length of this read */ /* movw (%si), %ax */ /* get number of sectors per track/head */ .byte 0x8b, 0x04 /* subb (%di), %al */ /* subtract sector start */ .byte 0x2a, 0x05 /* how many do we really want to read? */ /* cmpw %ax, 4(%di) */ /* compare against total number of sectors */ .byte 0x39, 0x45, 0x04 /* jg more_sectors (8-bit)*/ /* which is greater? */ .byte 0x7f; RELB(more_sectors) /* movw 4(%di), %ax */ /* if less than, set to total */ .byte 0x8b, 0x45, 0x04 more_sectors: /* subw %ax, 4(%di) */ /* subtract from total */ .byte 0x29, 0x45, 0x04 /* * This is the loop for taking care of BIOS geometry translation (ugh!) */ /* movb 3(%di), %dl */ /* get high bits of cylinder */ .byte 0x8a, 0x55, 0x03 shlb $6, %dl /* shift left by 6 bits */ /* movw (%di), %cl */ /* get sector */ .byte 0x8b, 0x0d incb %cl /* normalize sector (sectors go from 1-N, not 0-(N-1) ) */ orb %dl, %cl /* composite together */ /* movb 2(%di), %ch */ /* sector+hcyl in cl, cylinder in ch */ .byte 0x8a, 0x6d, 0x02 /* restore %dx */ popw %dx pushw %dx /* movb 1(%di), %dh */ /* head num */ .byte 0x8a, 0x75, 0x01 pushw %ax /* save %ax from destruction! */ /* * 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 $BUFFERSEG, %bx movw %bx, %es /* load %es segment with disk buffer */ xorw %bx, %bx /* %bx = 0, put it at 0 in the segment */ movb $0x2, %ah /* function 2 */ int $0x13 /* jc read_error */ .byte 0x72; RELB(read_error) movw %es, %ax movw %ax, %fs /* load source segment */ /* load addresses for copy from disk buffer to destination */ /* movw 6(%di), %es */ /* load destination segment */ .byte 0x8e, 0x45, 0x06 /* restore %ax */ popw %ax /* determine the next possible destination address (presuming 512 byte sectors!) */ shlw $5, %ax /* shift %ax five bits to the left */ /* addw %ax, 6(%di) */ /* add the corrected value to the destination address for next time */ .byte 0x01, 0x45, 0x06 /* get the copy length */ shlw $4, %ax movw %ax, %cx /* save addressing regs */ pushw %si pushw %di xorw %di, %di /* zero offset of destination addresses */ xorw %si, %si /* zero offset of source addresses */ cld /* sets the copy direction to forward */ /* perform copy */ rep /* sets a repeat */ fs /* this overrides the source segment from %ds to %fs */ movsb /* this runs the actual copy */ /* restore addressing regs */ popw %di popw %si /* check if finished with this dataset */ /* cmpw $0, 4(%di) */ .byte 0x81, 0x7d, 0x04; .word 0 /* je bootloop */ .byte 0x0f, 0x84; REL(bootloop) /* find out the next BIOS set to load in */ /* movb $0, (%di) */ /* set the sector start */ .byte 0xc6, 0x05, 0 xorb %ah, %ah /* zero %ah */ /* movb 1(%di), %al */ /* load head number into %al */ .byte 0x8a, 0x45, 0x01 incw %ax /* increment current head number */ /* cmpw 2(%si), %ax */ /* compare to total number of heads */ .byte 0x3b, 0x44, 0x02 /* jne update_heads (8-bit)*/ .byte 0x75; RELB(update_heads) /* movw 2(%di), %ax */ /* load cylinder number into %ax */ .byte 0x8b, 0x45, 0x02 incw %ax /* increment current cylinder number */ /* cmpw 4(%si), %ax */ /* compare to total number of cylinders */ .byte 0x3b, 0x44, 0x04 /* je geometry_error */ /* display error and die if greater */ .byte 0x74; RELB(geometry_error) /* movw %ax, 2(%di) */ /* store new cylinder number */ .byte 0x89, 0x45, 0x02 movb $0, %al /* for storing new head number */ update_heads: /* movb %al, 1(%di) */ /* store new head number */ .byte 0x88, 0x45, 0x01 /* jump to "setup_sectors" to determine length of the new read */ /* jmp setup_sectors */ .byte 0xe9; REL(setup_sectors) /* END OF MAIN LOOP */ /* * BIOS Geometry translation error (past the end of the disk geometry!). */ geometry_error: movw $geometry_error_string, %si /* call message */ .byte 0xe8; REL(message) /* jmp display_error (8-bit) */ .byte 0xeb; RELB(general_error) /* * Disk probe failure. */ hd_probe_error: movw $hd_probe_error_string, %si /* call message */ .byte 0xe8; REL(message) /* jmp display_error (8-bit) */ .byte 0xeb; RELB(general_error) /* * Read error on the disk. */ read_error: movw $read_error_string, %si /* call message */ .byte 0xe8; REL(message) general_error: movw $general_error_string, %si /* call message */ .byte 0xe8; REL(message) /* go here when you need to stop the machine hard after an error condition */ stop: /* jmp stop (8-bit) */ .byte 0xeb; RELB(stop) 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 */ .byte 0x8a, 0x4 cmpb $0, %al /* jne 1b */ .byte 0x75; RELB(1b) /* if not end of string, jmp to display */ /* ret */ .byte 0xc3 lastlist: /* * This area is an empty space between the main body of code below which * grows up (fixed after compilation, but between releases it may change * in size easily), and the lists of sectors to read, which grows down * from a fixed top location. */ /* * This data area is for keeping general parameters. */ . = _start + PARTSTART - MINPARAMSIZ - LISTSIZ /* this next data area before the partition area is specifically sized, you should update "MINPARAMSIZ" to reflect any additions or deletions to this area */ .word 0 .word 0 /* fill the first data listing with the default */ #ifdef FFS_STAGE1_5 .long 2 /* this is the sector start parameter, in logical sectors from the start of the disk, sector 0 */ .word 14 /* this is the number of sectors to read */ .word 0x0200 /* this is the segment of the starting address to load the data into */ #else .long 1 /* this is the sector start parameter, in logical sectors from the start of the disk, sector 0 */ .word 80 /* this is the number of sectors to read */ .word 0x0800 /* this is the segment of the starting address to load the data into */ #endif firstlist: /* this label has to be after the list data!!! */ .byte 0xff /* the disk to load stage2 from */ /* 0xff means use the boot drive */ /* * Jump here when all data loading is done. This * goes to the second stage bootloader. */ bootit: popw %dx /* this makes sure %dl is our "boot" drive */ /* ljmp $myoffset, $myseg */ .byte 0xea #ifdef STAGE1_5 .word 0x2000, 0 #else .word 0x8000, 0 #endif /* * This is the compatibility version number. * * DO NOT MOVE THIS!!! */ .byte 2, 0 /* * 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 + 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 */ .byte 0x8a, 0x0c /* if number of sectors is 0, display error and die */ cmpb $0, %cl /* jne 1f (8-bit) */ .byte 0x75; RELB(1f) /* * Floppy disk probe failure. */ movw $fd_probe_error_string, %si /* call message */ .byte 0xe8; REL(message) /* jmp display_error (16-bit) */ .byte 0xe9; REL(general_error) fd_probe_error_string: .string "Floppy" 1: /* perform read */ movw $REALSTACK, %bx movw $0x201, %ax movb $0, %ch movb $0, %dh int $0x13 /* if error, jump to "probe_loop" */ /* jc probe_loop (8-bit)*/ .byte 0x72; RELB(probe_loop) /* %cl is already the correct value! */ movb $1, %dh movb $79, %ch /* jmp final_init */ .byte 0xe9; REL(final_init) . = _start + PARTEND /* the last 2 bytes in the sector 0 contain the signature */ .word SIGNATURE