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